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