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