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