xref: /libCEED/interface/ceed-operator.c (revision 9a00868b5066558afb121f7066045d7511a0e1e3)
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     return CeedError(op->ceed, 1, "CEED_ELEMRESTRICTION_NONE can only be used "
686                      "for a field with eval mode CEED_EVAL_WEIGHT");
687   ierr = CeedCalloc(1, ofield); CeedChk(ierr);
688   (*ofield)->Erestrict = r;
689   r->refcount += 1;
690   (*ofield)->basis = b;
691   if (b != CEED_BASIS_COLLOCATED)
692     b->refcount += 1;
693   (*ofield)->vec = v;
694   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE)
695     v->refcount += 1;
696   op->nfields += 1;
697   return 0;
698 }
699 
700 /**
701   @brief Add a sub-operator to a composite CeedOperator
702 
703   @param[out] compositeop Composite CeedOperator
704   @param      subop       Sub-operator CeedOperator
705 
706   @return An error code: 0 - success, otherwise - failure
707 
708   @ref User
709  */
710 int CeedCompositeOperatorAddSub(CeedOperator compositeop, CeedOperator subop) {
711   if (!compositeop->composite)
712     // LCOV_EXCL_START
713     return CeedError(compositeop->ceed, 1, "CeedOperator is not a composite "
714                      "operator");
715   // LCOV_EXCL_STOP
716 
717   if (compositeop->numsub == CEED_COMPOSITE_MAX)
718     // LCOV_EXCL_START
719     return CeedError(compositeop->ceed, 1, "Cannot add additional suboperators");
720   // LCOV_EXCL_STOP
721 
722   compositeop->suboperators[compositeop->numsub] = subop;
723   subop->refcount++;
724   compositeop->numsub++;
725   return 0;
726 }
727 
728 /**
729   @brief Assemble a linear CeedQFunction associated with a CeedOperator
730 
731   This returns a CeedVector containing a matrix at each quadrature point
732     providing the action of the CeedQFunction associated with the CeedOperator.
733     The vector 'assembled' is of shape
734       [num_elements, num_input_fields, num_output_fields, num_quad_points]
735     and contains column-major matrices representing the action of the
736     CeedQFunction for a corresponding quadrature point on an element. Inputs and
737     outputs are in the order provided by the user when adding CeedOperator fields.
738     For example, a QFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
739     'v', provided in that order, would result in an assembled QFunction that
740     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
741     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
742 
743   @param op             CeedOperator to assemble CeedQFunction
744   @param[out] assembled CeedVector to store assembled CeedQFunction at
745                           quadrature points
746   @param[out] rstr      CeedElemRestriction for CeedVector containing assembled
747                           CeedQFunction
748   @param request        Address of CeedRequest for non-blocking completion, else
749                           CEED_REQUEST_IMMEDIATE
750 
751   @return An error code: 0 - success, otherwise - failure
752 
753   @ref User
754 **/
755 int CeedOperatorAssembleLinearQFunction(CeedOperator op, CeedVector *assembled,
756                                         CeedElemRestriction *rstr,
757                                         CeedRequest *request) {
758   int ierr;
759   Ceed ceed = op->ceed;
760   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
761 
762   // Backend version
763   if (op->AssembleLinearQFunction) {
764     ierr = op->AssembleLinearQFunction(op, assembled, rstr, request);
765     CeedChk(ierr);
766   } else {
767     // Fallback to reference Ceed
768     if (!op->opfallback) {
769       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
770     }
771     // Assemble
772     ierr = op->opfallback->AssembleLinearQFunction(op->opfallback, assembled,
773            rstr, request); CeedChk(ierr);
774   }
775 
776   return 0;
777 }
778 
779 /**
780   @brief Assemble the diagonal of a square linear Operator
781 
782   This returns a CeedVector containing the diagonal of a linear CeedOperator.
783 
784   @param op             CeedOperator to assemble CeedQFunction
785   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
786   @param request        Address of CeedRequest for non-blocking completion, else
787                           CEED_REQUEST_IMMEDIATE
788 
789   @return An error code: 0 - success, otherwise - failure
790 
791   @ref User
792 **/
793 int CeedOperatorAssembleLinearDiagonal(CeedOperator op, CeedVector *assembled,
794                                        CeedRequest *request) {
795   int ierr;
796   Ceed ceed = op->ceed;
797   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
798 
799   // Use backend version, if available
800   if (op->AssembleLinearDiagonal) {
801     ierr = op->AssembleLinearDiagonal(op, assembled, request); CeedChk(ierr);
802   } else {
803     // Fallback to reference Ceed
804     if (!op->opfallback) {
805       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
806     }
807     // Assemble
808     ierr = op->opfallback->AssembleLinearDiagonal(op->opfallback, assembled,
809            request); CeedChk(ierr);
810   }
811 
812   return 0;
813 }
814 
815 /**
816   @brief Build a FDM based approximate inverse for each element for a
817            CeedOperator
818 
819   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization
820     Method based approximate inverse. This function obtains the simultaneous
821     diagonalization for the 1D mass and Laplacian operators,
822       M = V^T V, K = V^T S V.
823     The assembled QFunction is used to modify the eigenvalues from simultaneous
824     diagonalization and obtain an approximate inverse of the form
825       V^T S^hat V. The CeedOperator must be linear and non-composite. The
826     associated CeedQFunction must therefore also be linear.
827 
828   @param op             CeedOperator to create element inverses
829   @param[out] fdminv    CeedOperator to apply the action of a FDM based inverse
830                           for each element
831   @param request        Address of CeedRequest for non-blocking completion, else
832                           CEED_REQUEST_IMMEDIATE
833 
834   @return An error code: 0 - success, otherwise - failure
835 
836   @ref Backend
837 **/
838 int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdminv,
839                                         CeedRequest *request) {
840   int ierr;
841   Ceed ceed = op->ceed;
842   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
843 
844   // Use backend version, if available
845   if (op->CreateFDMElementInverse) {
846     ierr = op->CreateFDMElementInverse(op, fdminv, request); CeedChk(ierr);
847   } else {
848     // Fallback to reference Ceed
849     if (!op->opfallback) {
850       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
851     }
852     // Assemble
853     ierr = op->opfallback->CreateFDMElementInverse(op->opfallback, fdminv,
854            request); CeedChk(ierr);
855   }
856 
857   return 0;
858 }
859 
860 /**
861   @brief View a CeedOperator
862 
863   @param[in] op     CeedOperator to view
864   @param[in] stream Stream to write; typically stdout/stderr or a file
865 
866   @return Error code: 0 - success, otherwise - failure
867 
868   @ref User
869 **/
870 int CeedOperatorView(CeedOperator op, FILE *stream) {
871   int ierr;
872 
873   if (op->composite) {
874     fprintf(stream, "Composite CeedOperator\n");
875 
876     for (CeedInt i=0; i<op->numsub; i++) {
877       fprintf(stream, "  SubOperator [%d]:\n", i);
878       ierr = CeedOperatorSingleView(op->suboperators[i], 1, stream);
879       CeedChk(ierr);
880     }
881   } else {
882     fprintf(stream, "CeedOperator\n");
883     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
884   }
885 
886   return 0;
887 }
888 
889 /**
890   @brief Apply CeedOperator to a vector
891 
892   This computes the action of the operator on the specified (active) input,
893   yielding its (active) output.  All inputs and outputs must be specified using
894   CeedOperatorSetField().
895 
896   @param op        CeedOperator to apply
897   @param[in] in    CeedVector containing input state or CEED_VECTOR_NONE if
898                   there are no active inputs
899   @param[out] out  CeedVector to store result of applying operator (must be
900                      distinct from @a in) or CEED_VECTOR_NONE if there are no
901                      active outputs
902   @param request   Address of CeedRequest for non-blocking completion, else
903                      CEED_REQUEST_IMMEDIATE
904 
905   @return An error code: 0 - success, otherwise - failure
906 
907   @ref User
908 **/
909 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
910                       CeedRequest *request) {
911   int ierr;
912   Ceed ceed = op->ceed;
913   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
914 
915   if (op->numelements)  {
916     // Standard Operator
917     if (op->Apply) {
918       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
919     } else {
920       // Zero all output vectors
921       CeedQFunction qf = op->qf;
922       for (CeedInt i=0; i<qf->numoutputfields; i++) {
923         CeedVector vec = op->outputfields[i]->vec;
924         if (vec == CEED_VECTOR_ACTIVE)
925           vec = out;
926         if (vec != CEED_VECTOR_NONE) {
927           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
928         }
929       }
930       // Apply
931       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
932     }
933   } else if (op->composite) {
934     // Composite Operator
935     if (op->ApplyComposite) {
936       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
937     } else {
938       CeedInt numsub;
939       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
940       CeedOperator *suboperators;
941       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
942 
943       // Zero all output vectors
944       if (out != CEED_VECTOR_NONE) {
945         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
946       }
947       for (CeedInt i=0; i<numsub; i++) {
948         for (CeedInt j=0; j<suboperators[i]->qf->numoutputfields; j++) {
949           CeedVector vec = suboperators[i]->outputfields[j]->vec;
950           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
951             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
952           }
953         }
954       }
955       // Apply
956       for (CeedInt i=0; i<op->numsub; i++) {
957         ierr = CeedOperatorApplyAdd(op->suboperators[i], in, out, request);
958         CeedChk(ierr);
959       }
960     }
961   }
962 
963   return 0;
964 }
965 
966 /**
967   @brief Apply CeedOperator to a vector and add result to output vector
968 
969   This computes the action of the operator on the specified (active) input,
970   yielding its (active) output.  All inputs and outputs must be specified using
971   CeedOperatorSetField().
972 
973   @param op        CeedOperator to apply
974   @param[in] in    CeedVector containing input state or NULL if there are no
975                      active inputs
976   @param[out] out  CeedVector to sum in result of applying operator (must be
977                      distinct from @a in) or NULL if there are no active outputs
978   @param request   Address of CeedRequest for non-blocking completion, else
979                      CEED_REQUEST_IMMEDIATE
980 
981   @return An error code: 0 - success, otherwise - failure
982 
983   @ref User
984 **/
985 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
986                          CeedRequest *request) {
987   int ierr;
988   Ceed ceed = op->ceed;
989   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
990 
991   if (op->numelements)  {
992     // Standard Operator
993     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
994   } else if (op->composite) {
995     // Composite Operator
996     if (op->ApplyAddComposite) {
997       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
998     } else {
999       CeedInt numsub;
1000       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
1001       CeedOperator *suboperators;
1002       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
1003 
1004       for (CeedInt i=0; i<numsub; i++) {
1005         ierr = CeedOperatorApplyAdd(suboperators[i], in, out, request);
1006         CeedChk(ierr);
1007       }
1008     }
1009   }
1010 
1011   return 0;
1012 }
1013 
1014 /**
1015   @brief Destroy a CeedOperator
1016 
1017   @param op CeedOperator to destroy
1018 
1019   @return An error code: 0 - success, otherwise - failure
1020 
1021   @ref User
1022 **/
1023 int CeedOperatorDestroy(CeedOperator *op) {
1024   int ierr;
1025 
1026   if (!*op || --(*op)->refcount > 0) return 0;
1027   if ((*op)->Destroy) {
1028     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1029   }
1030   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1031   // Free fields
1032   for (int i=0; i<(*op)->nfields; i++)
1033     if ((*op)->inputfields[i]) {
1034       if ((*op)->inputfields[i]->Erestrict != CEED_ELEMRESTRICTION_NONE) {
1035         ierr = CeedElemRestrictionDestroy(&(*op)->inputfields[i]->Erestrict);
1036         CeedChk(ierr);
1037       }
1038       if ((*op)->inputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1039         ierr = CeedBasisDestroy(&(*op)->inputfields[i]->basis); CeedChk(ierr);
1040       }
1041       if ((*op)->inputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1042           (*op)->inputfields[i]->vec != CEED_VECTOR_NONE ) {
1043         ierr = CeedVectorDestroy(&(*op)->inputfields[i]->vec); CeedChk(ierr);
1044       }
1045       ierr = CeedFree(&(*op)->inputfields[i]); CeedChk(ierr);
1046     }
1047   for (int i=0; i<(*op)->nfields; i++)
1048     if ((*op)->outputfields[i]) {
1049       ierr = CeedElemRestrictionDestroy(&(*op)->outputfields[i]->Erestrict);
1050       CeedChk(ierr);
1051       if ((*op)->outputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1052         ierr = CeedBasisDestroy(&(*op)->outputfields[i]->basis); CeedChk(ierr);
1053       }
1054       if ((*op)->outputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1055           (*op)->outputfields[i]->vec != CEED_VECTOR_NONE ) {
1056         ierr = CeedVectorDestroy(&(*op)->outputfields[i]->vec); CeedChk(ierr);
1057       }
1058       ierr = CeedFree(&(*op)->outputfields[i]); CeedChk(ierr);
1059     }
1060   // Destroy suboperators
1061   for (int i=0; i<(*op)->numsub; i++)
1062     if ((*op)->suboperators[i]) {
1063       ierr = CeedOperatorDestroy(&(*op)->suboperators[i]); CeedChk(ierr);
1064     }
1065   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1066   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1067   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1068 
1069   // Destroy fallback
1070   if ((*op)->opfallback) {
1071     ierr = (*op)->qffallback->Destroy((*op)->qffallback); CeedChk(ierr);
1072     ierr = CeedFree(&(*op)->qffallback); CeedChk(ierr);
1073     ierr = (*op)->opfallback->Destroy((*op)->opfallback); CeedChk(ierr);
1074     ierr = CeedFree(&(*op)->opfallback); CeedChk(ierr);
1075   }
1076 
1077   ierr = CeedFree(&(*op)->inputfields); CeedChk(ierr);
1078   ierr = CeedFree(&(*op)->outputfields); CeedChk(ierr);
1079   ierr = CeedFree(&(*op)->suboperators); CeedChk(ierr);
1080   ierr = CeedFree(op); CeedChk(ierr);
1081   return 0;
1082 }
1083 
1084 /// @}
1085