xref: /libCEED/interface/ceed-operator.c (revision cae8b89ab4384ccb591c7143fd0ca8a192ac9d03)
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 and overwrite output 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     if (op->Apply) {
626       ierr = op->Apply(op, in != CEED_VECTOR_NONE ? in : NULL,
627                        out != CEED_VECTOR_NONE ? out : NULL, request);
628       CeedChk(ierr);
629     } else {
630       // Zero all output vectors
631       if (!op->composite) {
632         for (CeedInt i=0; i<qf->numoutputfields; i++) {
633           CeedVector vec = op->outputfields[i]->vec;
634           if (vec == CEED_VECTOR_ACTIVE)
635             vec = out;
636           if (vec != CEED_VECTOR_NONE) {
637             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
638           }
639         }
640       } else if (out != CEED_VECTOR_NONE) { // Zero active output if composite
641         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
642       }
643       ierr = op->ApplyAdd(op, in != CEED_VECTOR_NONE ? in : NULL,
644                           out != CEED_VECTOR_NONE ? out : NULL, request);
645       CeedChk(ierr);
646     }
647   }
648   return 0;
649 }
650 
651 /**
652   @brief Apply CeedOperator to a vector and add result to output vector
653 
654   This computes the action of the operator on the specified (active) input,
655   yielding its (active) output.  All inputs and outputs must be specified using
656   CeedOperatorSetField().
657 
658   @param op        CeedOperator to apply
659   @param[in] in    CeedVector containing input state or NULL if there are no
660                      active inputs
661   @param[out] out  CeedVector to sum in result of applying operator (must be
662                      distinct from @a in) or NULL if there are no active outputs
663   @param request   Address of CeedRequest for non-blocking completion, else
664                      CEED_REQUEST_IMMEDIATE
665 
666   @return An error code: 0 - success, otherwise - failure
667 
668   @ref Basic
669 **/
670 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
671                          CeedRequest *request) {
672   int ierr;
673   Ceed ceed = op->ceed;
674   CeedQFunction qf = op->qf;
675 
676   if (op->composite) {
677     if (!op->numsub)
678       // LCOV_EXCL_START
679       return CeedError(ceed, 1, "No suboperators set");
680     // LCOV_EXCL_STOP
681   } else {
682     if (op->nfields == 0)
683       // LCOV_EXCL_START
684       return CeedError(ceed, 1, "No operator fields set");
685     // LCOV_EXCL_STOP
686     if (op->nfields < qf->numinputfields + qf->numoutputfields)
687       // LCOV_EXCL_START
688       return CeedError(ceed, 1, "Not all operator fields set");
689     // LCOV_EXCL_STOP
690     if (!op->hasrestriction)
691       // LCOV_EXCL_START
692       return CeedError(ceed, 1,"At least one restriction required");
693     // LCOV_EXCL_STOP
694     if (op->numqpoints == 0)
695       // LCOV_EXCL_START
696       return CeedError(ceed, 1,"At least one non-collocated basis required");
697     // LCOV_EXCL_STOP
698   }
699   if (op->numelements || op->composite) {
700     ierr = op->ApplyAdd(op, in != CEED_VECTOR_NONE ? in : NULL,
701                         out != CEED_VECTOR_NONE ? out : NULL, request);
702     CeedChk(ierr);
703   }
704   return 0;
705 }
706 
707 /**
708   @brief Get the Ceed associated with a CeedOperator
709 
710   @param op              CeedOperator
711   @param[out] ceed       Variable to store Ceed
712 
713   @return An error code: 0 - success, otherwise - failure
714 
715   @ref Advanced
716 **/
717 
718 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
719   *ceed = op->ceed;
720   return 0;
721 }
722 
723 /**
724   @brief Get the number of elements associated with a CeedOperator
725 
726   @param op              CeedOperator
727   @param[out] numelem    Variable to store number of elements
728 
729   @return An error code: 0 - success, otherwise - failure
730 
731   @ref Advanced
732 **/
733 
734 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *numelem) {
735   if (op->composite)
736     // LCOV_EXCL_START
737     return CeedError(op->ceed, 1, "Not defined for composite operator");
738   // LCOV_EXCL_STOP
739 
740   *numelem = op->numelements;
741   return 0;
742 }
743 
744 /**
745   @brief Get the number of quadrature points associated with a CeedOperator
746 
747   @param op              CeedOperator
748   @param[out] numqpts    Variable to store vector number of quadrature points
749 
750   @return An error code: 0 - success, otherwise - failure
751 
752   @ref Advanced
753 **/
754 
755 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *numqpts) {
756   if (op->composite)
757     // LCOV_EXCL_START
758     return CeedError(op->ceed, 1, "Not defined for composite operator");
759   // LCOV_EXCL_STOP
760 
761   *numqpts = op->numqpoints;
762   return 0;
763 }
764 
765 /**
766   @brief Get the number of arguments associated with a CeedOperator
767 
768   @param op              CeedOperator
769   @param[out] numargs    Variable to store vector number of arguments
770 
771   @return An error code: 0 - success, otherwise - failure
772 
773   @ref Advanced
774 **/
775 
776 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *numargs) {
777   if (op->composite)
778     // LCOV_EXCL_START
779     return CeedError(op->ceed, 1, "Not defined for composite operators");
780   // LCOV_EXCL_STOP
781 
782   *numargs = op->nfields;
783   return 0;
784 }
785 
786 /**
787   @brief Get the setup status of a CeedOperator
788 
789   @param op             CeedOperator
790   @param[out] setupdone Variable to store setup status
791 
792   @return An error code: 0 - success, otherwise - failure
793 
794   @ref Advanced
795 **/
796 
797 int CeedOperatorGetSetupStatus(CeedOperator op, bool *setupdone) {
798   *setupdone = op->setupdone;
799   return 0;
800 }
801 
802 /**
803   @brief Get the QFunction associated with a CeedOperator
804 
805   @param op              CeedOperator
806   @param[out] qf         Variable to store QFunction
807 
808   @return An error code: 0 - success, otherwise - failure
809 
810   @ref Advanced
811 **/
812 
813 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
814   if (op->composite)
815     // LCOV_EXCL_START
816     return CeedError(op->ceed, 1, "Not defined for composite operator");
817   // LCOV_EXCL_STOP
818 
819   *qf = op->qf;
820   return 0;
821 }
822 
823 /**
824   @brief Get the number of suboperators associated with a CeedOperator
825 
826   @param op              CeedOperator
827   @param[out] numsub     Variable to store number of suboperators
828 
829   @return An error code: 0 - success, otherwise - failure
830 
831   @ref Advanced
832 **/
833 
834 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *numsub) {
835   if (!op->composite)
836     // LCOV_EXCL_START
837     return CeedError(op->ceed, 1, "Not a composite operator");
838   // LCOV_EXCL_STOP
839 
840   *numsub = op->numsub;
841   return 0;
842 }
843 
844 /**
845   @brief Get the list of suboperators associated with a CeedOperator
846 
847   @param op                CeedOperator
848   @param[out] suboperators Variable to store list of suboperators
849 
850   @return An error code: 0 - success, otherwise - failure
851 
852   @ref Advanced
853 **/
854 
855 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **suboperators) {
856   if (!op->composite)
857     // LCOV_EXCL_START
858     return CeedError(op->ceed, 1, "Not a composite operator");
859   // LCOV_EXCL_STOP
860 
861   *suboperators = op->suboperators;
862   return 0;
863 }
864 
865 /**
866   @brief Set the backend data of a CeedOperator
867 
868   @param[out] op         CeedOperator
869   @param data            Data to set
870 
871   @return An error code: 0 - success, otherwise - failure
872 
873   @ref Advanced
874 **/
875 
876 int CeedOperatorSetData(CeedOperator op, void **data) {
877   op->data = *data;
878   return 0;
879 }
880 
881 /**
882   @brief Get the backend data of a CeedOperator
883 
884   @param op              CeedOperator
885   @param[out] data       Variable to store data
886 
887   @return An error code: 0 - success, otherwise - failure
888 
889   @ref Advanced
890 **/
891 
892 int CeedOperatorGetData(CeedOperator op, void **data) {
893   *data = op->data;
894   return 0;
895 }
896 
897 /**
898   @brief Set the setup flag of a CeedOperator to True
899 
900   @param op              CeedOperator
901 
902   @return An error code: 0 - success, otherwise - failure
903 
904   @ref Advanced
905 **/
906 
907 int CeedOperatorSetSetupDone(CeedOperator op) {
908   op->setupdone = 1;
909   return 0;
910 }
911 
912 /**
913   @brief View a field of a CeedOperator
914 
915   @param[in] field       Operator field to view
916   @param[in] fieldnumber Number of field being viewed
917   @param[in] stream      Stream to view to, e.g., stdout
918 
919   @return An error code: 0 - success, otherwise - failure
920 
921   @ref Utility
922 **/
923 static int CeedOperatorFieldView(CeedOperatorField field,
924                                  CeedQFunctionField qffield,
925                                  CeedInt fieldnumber, bool sub, bool in,
926                                  FILE *stream) {
927   const char *pre = sub ? "  " : "";
928   const char *inout = in ? "Input" : "Output";
929 
930   fprintf(stream, "%s    %s Field [%d]:\n"
931           "%s      Name: \"%s\"\n"
932           "%s      Lmode: \"%s\"\n",
933           pre, inout, fieldnumber, pre, qffield->fieldname,
934           pre, CeedTransposeModes[field->lmode]);
935 
936   if (field->basis == CEED_BASIS_COLLOCATED)
937     fprintf(stream, "%s      Collocated basis\n", pre);
938 
939   if (field->vec == CEED_VECTOR_ACTIVE)
940     fprintf(stream, "%s      Active vector\n", pre);
941   else if (field->vec == CEED_VECTOR_NONE)
942     fprintf(stream, "%s      No vector\n", pre);
943 
944   return 0;
945 }
946 
947 /**
948   @brief View a single CeedOperator
949 
950   @param[in] op     CeedOperator to view
951   @param[in] stream Stream to write; typically stdout/stderr or a file
952 
953   @return Error code: 0 - success, otherwise - failure
954 
955   @ref Utility
956 **/
957 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
958   int ierr;
959   const char *pre = sub ? "  " : "";
960 
961   CeedInt totalfields;
962   ierr = CeedOperatorGetNumArgs(op, &totalfields); CeedChk(ierr);
963 
964   fprintf(stream, "%s  %d Field%s\n", pre, totalfields, totalfields>1 ? "s" : "");
965 
966   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->numinputfields,
967           op->qf->numinputfields>1 ? "s" : "");
968   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
969     ierr = CeedOperatorFieldView(op->inputfields[i], op->qf->inputfields[i],
970                                  i, sub, 1, stream); CeedChk(ierr);
971   }
972 
973   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->numoutputfields,
974           op->qf->numoutputfields>1 ? "s" : "");
975   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
976     ierr = CeedOperatorFieldView(op->outputfields[i], op->qf->inputfields[i],
977                                  i, sub, 0, stream); CeedChk(ierr);
978   }
979 
980   return 0;
981 }
982 
983 /**
984   @brief View a CeedOperator
985 
986   @param[in] op     CeedOperator to view
987   @param[in] stream Stream to write; typically stdout/stderr or a file
988 
989   @return Error code: 0 - success, otherwise - failure
990 
991   @ref Utility
992 **/
993 int CeedOperatorView(CeedOperator op, FILE *stream) {
994   int ierr;
995 
996   if (op->composite) {
997     fprintf(stream, "Composite CeedOperator\n");
998 
999     for (CeedInt i=0; i<op->numsub; i++) {
1000       fprintf(stream, "  SubOperator [%d]:\n", i);
1001       ierr = CeedOperatorSingleView(op->suboperators[i], 1, stream);
1002       CeedChk(ierr);
1003     }
1004   } else {
1005     fprintf(stream, "CeedOperator\n");
1006     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1007   }
1008 
1009   return 0;
1010 }
1011 
1012 /**
1013   @brief Get the CeedOperatorFields of a CeedOperator
1014 
1015   @param op                 CeedOperator
1016   @param[out] inputfields   Variable to store inputfields
1017   @param[out] outputfields  Variable to store outputfields
1018 
1019   @return An error code: 0 - success, otherwise - failure
1020 
1021   @ref Advanced
1022 **/
1023 
1024 int CeedOperatorGetFields(CeedOperator op, CeedOperatorField **inputfields,
1025                           CeedOperatorField **outputfields) {
1026   if (op->composite)
1027     // LCOV_EXCL_START
1028     return CeedError(op->ceed, 1, "Not defined for composite operator");
1029   // LCOV_EXCL_STOP
1030 
1031   if (inputfields) *inputfields = op->inputfields;
1032   if (outputfields) *outputfields = op->outputfields;
1033   return 0;
1034 }
1035 
1036 /**
1037   @brief Get the L vector CeedTransposeMode of a CeedOperatorField
1038 
1039   @param opfield         CeedOperatorField
1040   @param[out] lmode      Variable to store CeedTransposeMode
1041 
1042   @return An error code: 0 - success, otherwise - failure
1043 
1044   @ref Advanced
1045 **/
1046 
1047 int CeedOperatorFieldGetLMode(CeedOperatorField opfield,
1048                               CeedTransposeMode *lmode) {
1049   *lmode = opfield->lmode;
1050   return 0;
1051 }
1052 
1053 /**
1054   @brief Get the CeedElemRestriction of a CeedOperatorField
1055 
1056   @param opfield         CeedOperatorField
1057   @param[out] rstr       Variable to store CeedElemRestriction
1058 
1059   @return An error code: 0 - success, otherwise - failure
1060 
1061   @ref Advanced
1062 **/
1063 
1064 int CeedOperatorFieldGetElemRestriction(CeedOperatorField opfield,
1065                                         CeedElemRestriction *rstr) {
1066   *rstr = opfield->Erestrict;
1067   return 0;
1068 }
1069 
1070 /**
1071   @brief Get the CeedBasis of a CeedOperatorField
1072 
1073   @param opfield         CeedOperatorField
1074   @param[out] basis      Variable to store CeedBasis
1075 
1076   @return An error code: 0 - success, otherwise - failure
1077 
1078   @ref Advanced
1079 **/
1080 
1081 int CeedOperatorFieldGetBasis(CeedOperatorField opfield, CeedBasis *basis) {
1082   *basis = opfield->basis;
1083   return 0;
1084 }
1085 
1086 /**
1087   @brief Get the CeedVector of a CeedOperatorField
1088 
1089   @param opfield         CeedOperatorField
1090   @param[out] vec        Variable to store CeedVector
1091 
1092   @return An error code: 0 - success, otherwise - failure
1093 
1094   @ref Advanced
1095 **/
1096 
1097 int CeedOperatorFieldGetVector(CeedOperatorField opfield, CeedVector *vec) {
1098   *vec = opfield->vec;
1099   return 0;
1100 }
1101 
1102 /**
1103   @brief Destroy a CeedOperator
1104 
1105   @param op CeedOperator to destroy
1106 
1107   @return An error code: 0 - success, otherwise - failure
1108 
1109   @ref Basic
1110 **/
1111 int CeedOperatorDestroy(CeedOperator *op) {
1112   int ierr;
1113 
1114   if (!*op || --(*op)->refcount > 0) return 0;
1115   if ((*op)->Destroy) {
1116     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1117   }
1118   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1119   // Free fields
1120   for (int i=0; i<(*op)->nfields; i++)
1121     if ((*op)->inputfields[i]) {
1122       ierr = CeedElemRestrictionDestroy(&(*op)->inputfields[i]->Erestrict);
1123       CeedChk(ierr);
1124       if ((*op)->inputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1125         ierr = CeedBasisDestroy(&(*op)->inputfields[i]->basis); CeedChk(ierr);
1126       }
1127       if ((*op)->inputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1128           (*op)->inputfields[i]->vec != CEED_VECTOR_NONE ) {
1129         ierr = CeedVectorDestroy(&(*op)->inputfields[i]->vec); CeedChk(ierr);
1130       }
1131       ierr = CeedFree(&(*op)->inputfields[i]); CeedChk(ierr);
1132     }
1133   for (int i=0; i<(*op)->nfields; i++)
1134     if ((*op)->outputfields[i]) {
1135       ierr = CeedElemRestrictionDestroy(&(*op)->outputfields[i]->Erestrict);
1136       CeedChk(ierr);
1137       if ((*op)->outputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1138         ierr = CeedBasisDestroy(&(*op)->outputfields[i]->basis); CeedChk(ierr);
1139       }
1140       if ((*op)->outputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1141           (*op)->outputfields[i]->vec != CEED_VECTOR_NONE ) {
1142         ierr = CeedVectorDestroy(&(*op)->outputfields[i]->vec); CeedChk(ierr);
1143       }
1144       ierr = CeedFree(&(*op)->outputfields[i]); CeedChk(ierr);
1145     }
1146   // Destroy suboperators
1147   for (int i=0; i<(*op)->numsub; i++)
1148     if ((*op)->suboperators[i]) {
1149       ierr = CeedOperatorDestroy(&(*op)->suboperators[i]); CeedChk(ierr);
1150     }
1151   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1152   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1153   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1154 
1155   // Destroy fallback
1156   if ((*op)->opfallback) {
1157     ierr = (*op)->qffallback->Destroy((*op)->qffallback); CeedChk(ierr);
1158     ierr = CeedFree(&(*op)->qffallback); CeedChk(ierr);
1159     ierr = (*op)->opfallback->Destroy((*op)->opfallback); CeedChk(ierr);
1160     ierr = CeedFree(&(*op)->opfallback); CeedChk(ierr);
1161   }
1162 
1163   ierr = CeedFree(&(*op)->inputfields); CeedChk(ierr);
1164   ierr = CeedFree(&(*op)->outputfields); CeedChk(ierr);
1165   ierr = CeedFree(&(*op)->suboperators); CeedChk(ierr);
1166   ierr = CeedFree(op); CeedChk(ierr);
1167   return 0;
1168 }
1169 
1170 /// @}
1171