xref: /libCEED/interface/ceed-operator.c (revision cdb3667fd8dbc356f6a9f05b2482cc0683957a96)
1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3 // reserved. See files LICENSE and NOTICE for details.
4 //
5 // This file is part of CEED, a collection of benchmarks, miniapps, software
6 // libraries and APIs for efficient high-order finite element and spectral
7 // element discretizations for exascale applications. For more information and
8 // source code availability see http://github.com/ceed.
9 //
10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11 // a collaborative effort of two U.S. Department of Energy organizations (Office
12 // of Science and the National Nuclear Security Administration) responsible for
13 // the planning and preparation of a capable exascale ecosystem, including
14 // software, applications, hardware, advanced system engineering and early
15 // testbed platforms, in support of the nation's exascale computing imperative.
16 
17 #include <ceed-impl.h>
18 #include <ceed-backend.h>
19 #include <string.h>
20 #include <math.h>
21 
22 /// @file
23 /// Implementation of public CeedOperator interfaces
24 ///
25 /// @addtogroup CeedOperator
26 ///   @{
27 
28 /**
29   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
30            CeedElemRestriction can be associated with CeedQFunction fields with
31            \ref CeedOperatorSetField.
32 
33   @param ceed    A Ceed object where the CeedOperator will be created
34   @param qf      QFunction defining the action of the operator at quadrature points
35   @param dqf     QFunction defining the action of the Jacobian of @a qf (or
36                    CEED_QFUNCTION_NONE)
37   @param dqfT    QFunction defining the action of the transpose of the Jacobian
38                    of @a qf (or CEED_QFUNCTION_NONE)
39   @param[out] op Address of the variable where the newly created
40                      CeedOperator will be stored
41 
42   @return An error code: 0 - success, otherwise - failure
43 
44   @ref Basic
45  */
46 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
47                        CeedQFunction dqfT, CeedOperator *op) {
48   int ierr;
49 
50   if (!ceed->OperatorCreate) {
51     Ceed delegate;
52     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
53 
54     if (!delegate)
55       // LCOV_EXCL_START
56       return CeedError(ceed, 1, "Backend does not support OperatorCreate");
57     // LCOV_EXCL_STOP
58 
59     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
60     return 0;
61   }
62 
63   ierr = CeedCalloc(1, op); CeedChk(ierr);
64   (*op)->ceed = ceed;
65   ceed->refcount++;
66   (*op)->refcount = 1;
67   if (qf == CEED_QFUNCTION_NONE)
68     // LCOV_EXCL_START
69     return CeedError(ceed, 1, "Operator must have a valid QFunction.");
70   // LCOV_EXCL_STOP
71   (*op)->qf = qf;
72   qf->refcount++;
73   if (dqf && dqf != CEED_QFUNCTION_NONE) {
74     (*op)->dqf = dqf;
75     dqf->refcount++;
76   }
77   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
78     (*op)->dqfT = dqfT;
79     dqfT->refcount++;
80   }
81   ierr = CeedCalloc(16, &(*op)->inputfields); CeedChk(ierr);
82   ierr = CeedCalloc(16, &(*op)->outputfields); CeedChk(ierr);
83   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
84   return 0;
85 }
86 
87 /**
88   @brief Create an operator that composes the action of several operators
89 
90   @param ceed    A Ceed object where the CeedOperator will be created
91   @param[out] op Address of the variable where the newly created
92                      Composite CeedOperator will be stored
93 
94   @return An error code: 0 - success, otherwise - failure
95 
96   @ref Basic
97  */
98 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
99   int ierr;
100 
101   if (!ceed->CompositeOperatorCreate) {
102     Ceed delegate;
103     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
104 
105     if (delegate) {
106       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
107       return 0;
108     }
109   }
110 
111   ierr = CeedCalloc(1, op); CeedChk(ierr);
112   (*op)->ceed = ceed;
113   ceed->refcount++;
114   (*op)->composite = true;
115   ierr = CeedCalloc(16, &(*op)->suboperators); CeedChk(ierr);
116 
117   if (ceed->CompositeOperatorCreate) {
118     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
119   }
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 Check if a CeedOperator is ready to be used.
314 
315   @param[in] ceed Ceed object for error handling
316   @param[in] op   CeedOperator to check
317 
318   @return An error code: 0 - success, otherwise - failure
319 
320   @ref Basic
321 **/
322 static int CeedOperatorCheckReady(Ceed ceed, CeedOperator op) {
323   CeedQFunction qf = op->qf;
324 
325   if (op->composite) {
326     if (!op->numsub)
327       // LCOV_EXCL_START
328       return CeedError(ceed, 1, "No suboperators set");
329     // LCOV_EXCL_STOP
330   } else {
331     if (op->nfields == 0)
332       // LCOV_EXCL_START
333       return CeedError(ceed, 1, "No operator fields set");
334     // LCOV_EXCL_STOP
335     if (op->nfields < qf->numinputfields + qf->numoutputfields)
336       // LCOV_EXCL_START
337       return CeedError(ceed, 1, "Not all operator fields set");
338     // LCOV_EXCL_STOP
339     if (!op->hasrestriction)
340       // LCOV_EXCL_START
341       return CeedError(ceed, 1,"At least one restriction required");
342     // LCOV_EXCL_STOP
343     if (op->numqpoints == 0)
344       // LCOV_EXCL_START
345       return CeedError(ceed, 1,"At least one non-collocated basis required");
346     // LCOV_EXCL_STOP
347   }
348 
349   return 0;
350 }
351 
352 /**
353   @brief Assemble a linear CeedQFunction associated with a CeedOperator
354 
355   This returns a CeedVector containing a matrix at each quadrature point
356     providing the action of the CeedQFunction associated with the CeedOperator.
357     The vector 'assembled' is of shape
358       [num_elements, num_input_fields, num_output_fields, num_quad_points]
359     and contains column-major matrices representing the action of the
360     CeedQFunction for a corresponding quadrature point on an element. Inputs and
361     outputs are in the order provided by the user when adding CeedOperator fields.
362     For example, a QFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
363     'v', provided in that order, would result in an assembled QFunction that
364     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
365     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
366 
367   @param op             CeedOperator to assemble CeedQFunction
368   @param[out] assembled CeedVector to store assembled CeedQFunction at
369                           quadrature points
370   @param[out] rstr      CeedElemRestriction for CeedVector containing assembled
371                           CeedQFunction
372   @param request        Address of CeedRequest for non-blocking completion, else
373                           CEED_REQUEST_IMMEDIATE
374 
375   @return An error code: 0 - success, otherwise - failure
376 
377   @ref Basic
378 **/
379 int CeedOperatorAssembleLinearQFunction(CeedOperator op, CeedVector *assembled,
380                                         CeedElemRestriction *rstr,
381                                         CeedRequest *request) {
382   int ierr;
383   Ceed ceed = op->ceed;
384   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
385 
386   // Backend version
387   if (op->AssembleLinearQFunction) {
388     ierr = op->AssembleLinearQFunction(op, assembled, rstr, request);
389     CeedChk(ierr);
390   } else {
391     // Fallback to reference Ceed
392     if (!op->opfallback) {
393       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
394     }
395     // Assemble
396     ierr = op->opfallback->AssembleLinearQFunction(op->opfallback, assembled,
397            rstr, request); CeedChk(ierr);
398   }
399 
400   return 0;
401 }
402 
403 /**
404   @brief Assemble the diagonal of a square linear Operator
405 
406   This returns a CeedVector containing the diagonal of a linear CeedOperator.
407 
408   @param op             CeedOperator to assemble CeedQFunction
409   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
410   @param request        Address of CeedRequest for non-blocking completion, else
411                           CEED_REQUEST_IMMEDIATE
412 
413   @return An error code: 0 - success, otherwise - failure
414 
415   @ref Basic
416 **/
417 int CeedOperatorAssembleLinearDiagonal(CeedOperator op, CeedVector *assembled,
418                                        CeedRequest *request) {
419   int ierr;
420   Ceed ceed = op->ceed;
421   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
422 
423   // Use backend version, if available
424   if (op->AssembleLinearDiagonal) {
425     ierr = op->AssembleLinearDiagonal(op, assembled, request); CeedChk(ierr);
426   } else {
427     // Fallback to reference Ceed
428     if (!op->opfallback) {
429       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
430     }
431     // Assemble
432     ierr = op->opfallback->AssembleLinearDiagonal(op->opfallback, assembled,
433            request); CeedChk(ierr);
434   }
435 
436   return 0;
437 }
438 
439 /**
440   @brief Build a FDM based approximate inverse for each element for a
441            CeedOperator
442 
443   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization
444     Method based approximate inverse. This function obtains the simultaneous
445     diagonalization for the 1D mass and Laplacian operators,
446       M = V^T V, K = V^T S V.
447     The assembled QFunction is used to modify the eigenvalues from simultaneous
448     diagonalization and obtain an approximate inverse of the form
449       V^T S^hat V. The CeedOperator must be linear and non-composite. The
450     associated CeedQFunction must therefore also be linear.
451 
452   @param op             CeedOperator to create element inverses
453   @param[out] fdminv    CeedOperator to apply the action of a FDM based inverse
454                           for each element
455   @param[out] qdata     CeedVector to hold qdata for fdminv
456   @param request        Address of CeedRequest for non-blocking completion, else
457                           CEED_REQUEST_IMMEDIATE
458 
459   @return An error code: 0 - success, otherwise - failure
460 
461   @ref Advanced
462 **/
463 int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdminv,
464                                         CeedRequest *request) {
465   int ierr;
466   Ceed ceed = op->ceed;
467 
468   // Determine active input basis
469   bool interp = false, grad = false;
470   CeedBasis basis = NULL;
471   CeedElemRestriction rstr = NULL;
472   for (CeedInt i=0; i<op->qf->numinputfields; i++)
473     if (op->inputfields[i] && op->inputfields[i]->vec == CEED_VECTOR_ACTIVE) {
474       basis = op->inputfields[i]->basis;
475       interp = interp || op->qf->inputfields[i]->emode == CEED_EVAL_INTERP;
476       grad = grad || op->qf->inputfields[i]->emode == CEED_EVAL_GRAD;
477       rstr = op->inputfields[i]->Erestrict;
478     }
479   if (!basis)
480     return CeedError(ceed, 1, "No active field set");
481   CeedInt P1d, Q1d, elemsize, nqpts, dim, ncomp = 1, nelem = 1, nnodes = 1;
482   ierr = CeedBasisGetNumNodes1D(basis, &P1d); CeedChk(ierr);
483   ierr = CeedBasisGetNumNodes(basis, &elemsize); CeedChk(ierr);
484   ierr = CeedBasisGetNumQuadraturePoints1D(basis, &Q1d); CeedChk(ierr);
485   ierr = CeedBasisGetNumQuadraturePoints(basis, &nqpts); CeedChk(ierr);
486   ierr = CeedBasisGetDimension(basis, &dim); CeedChk(ierr);
487   ierr = CeedBasisGetNumComponents(basis, &ncomp); CeedChk(ierr);
488   ierr = CeedElemRestrictionGetNumElements(rstr, &nelem); CeedChk(ierr);
489   ierr = CeedElemRestrictionGetNumNodes(rstr, &nnodes); CeedChk(ierr);
490 
491   // Build and diagonalize 1D Mass and Laplacian
492   if (!basis->tensorbasis)
493     return CeedError(ceed, 1, "FDMElementInverse only supported for tensor "
494                      "bases");
495   CeedScalar *work, *mass, *laplace, *x, *x2, *lambda;
496   ierr = CeedMalloc(Q1d*P1d, &work); CeedChk(ierr);
497   ierr = CeedMalloc(P1d*P1d, &mass); CeedChk(ierr);
498   ierr = CeedMalloc(P1d*P1d, &laplace); CeedChk(ierr);
499   ierr = CeedMalloc(P1d*P1d, &x); CeedChk(ierr);
500   ierr = CeedMalloc(P1d*P1d, &x2); CeedChk(ierr);
501   ierr = CeedMalloc(P1d, &lambda); CeedChk(ierr);
502   // -- Mass
503   for (CeedInt i=0; i<Q1d; i++)
504     for (CeedInt j=0; j<P1d; j++)
505       work[i+j*Q1d] = basis->interp1d[i*P1d+j]*basis->qweight1d[i];
506   ierr = CeedMatrixMultiply(ceed, work, basis->interp1d, mass, P1d, P1d, Q1d);
507   CeedChk(ierr);
508   // -- Laplacian
509   for (CeedInt i=0; i<Q1d; i++)
510     for (CeedInt j=0; j<P1d; j++)
511       work[i+j*Q1d] = basis->grad1d[i*P1d+j]*basis->qweight1d[i];
512   ierr = CeedMatrixMultiply(ceed, work, basis->grad1d, laplace, P1d, P1d, Q1d);
513   CeedChk(ierr);
514   // -- Diagonalize
515   ierr = CeedSimultaneousDiagonalization(ceed, laplace, mass, x, lambda, P1d);
516   CeedChk(ierr);
517   ierr = CeedFree(&work); CeedChk(ierr);
518   ierr = CeedFree(&mass); CeedChk(ierr);
519   ierr = CeedFree(&laplace); CeedChk(ierr);
520   for (CeedInt i=0; i<P1d; i++)
521     for (CeedInt j=0; j<P1d; j++)
522       x2[i+j*P1d] = x[j+i*P1d];
523   ierr = CeedFree(&x); CeedChk(ierr);
524 
525   // Assemble QFunction
526   CeedVector assembled;
527   CeedElemRestriction rstr_qf;
528   ierr =  CeedOperatorAssembleLinearQFunction(op, &assembled, &rstr_qf,
529           request); CeedChk(ierr);
530   ierr = CeedElemRestrictionDestroy(&rstr_qf); CeedChk(ierr);
531 
532   // Calculate element averages
533   CeedInt nfields = ((interp?1:0) + (grad?dim:0))*((interp?1:0) + (grad?dim:0));
534   CeedScalar *elemavg;
535   const CeedScalar *assembledarray, *qweightsarray;
536   CeedVector qweights;
537   ierr = CeedVectorCreate(ceed, nqpts, &qweights); CeedChk(ierr);
538   ierr = CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_WEIGHT,
539                         CEED_VECTOR_NONE, qweights); CeedChk(ierr);
540   ierr = CeedVectorGetArrayRead(assembled, CEED_MEM_HOST, &assembledarray);
541   CeedChk(ierr);
542   ierr = CeedVectorGetArrayRead(qweights, CEED_MEM_HOST, &qweightsarray);
543   CeedChk(ierr);
544   ierr = CeedCalloc(nelem, &elemavg); CeedChk(ierr);
545   for (CeedInt e=0; e<nelem; e++) {
546     CeedInt count = 0;
547     for (CeedInt q=0; q<nqpts; q++)
548       for (CeedInt i=0; i<ncomp*ncomp*nfields; i++)
549         if (fabs(assembledarray[e*nelem*nqpts*ncomp*ncomp*nfields +
550                                                                   i*nqpts + q]) > CEED_EPSILON) {
551           elemavg[e] += assembledarray[e*nelem*nqpts*ncomp*ncomp*nfields +
552                                        i*nqpts + q] / qweightsarray[q];
553           count++;
554         }
555     if (count)
556       elemavg[e] /= count;
557   }
558   ierr = CeedVectorRestoreArrayRead(assembled, &assembledarray); CeedChk(ierr);
559   ierr = CeedVectorDestroy(&assembled); CeedChk(ierr);
560   ierr = CeedVectorRestoreArrayRead(qweights, &qweightsarray); CeedChk(ierr);
561   ierr = CeedVectorDestroy(&qweights); CeedChk(ierr);
562 
563   // Build FDM diagonal
564   CeedVector qdata;
565   CeedScalar *qdataarray;
566   ierr = CeedVectorCreate(ceed, nelem*ncomp*nnodes, &qdata); CeedChk(ierr);
567   ierr = CeedVectorSetArray(qdata, CEED_MEM_HOST, CEED_COPY_VALUES, NULL);
568   CeedChk(ierr);
569   ierr = CeedVectorGetArray(qdata, CEED_MEM_HOST, &qdataarray); CeedChk(ierr);
570   for (CeedInt e=0; e<nelem; e++)
571     for (CeedInt c=0; c<ncomp; c++)
572       for (CeedInt n=0; n<nnodes; n++) {
573         if (interp)
574           qdataarray[(e*ncomp+c)*nnodes+n] = 1;
575         if (grad)
576           for (CeedInt d=0; d<dim; d++) {
577             CeedInt i = (n / CeedIntPow(P1d, d)) % P1d;
578             qdataarray[(e*ncomp+c)*nnodes+n] += lambda[i];
579           }
580         qdataarray[(e*ncomp+c)*nnodes+n] = 1 / (elemavg[e] *
581                                                 qdataarray[(e*ncomp+c)*nnodes+n]);
582       }
583   ierr = CeedFree(&elemavg); CeedChk(ierr);
584   ierr = CeedVectorRestoreArray(qdata, &qdataarray); CeedChk(ierr);
585 
586   // Setup FDM operator
587   // -- Basis
588   CeedBasis fdm_basis;
589   CeedScalar *graddummy, *qrefdummy, *qweightdummy;
590   ierr = CeedCalloc(P1d*P1d, &graddummy); CeedChk(ierr);
591   ierr = CeedCalloc(P1d, &qrefdummy); CeedChk(ierr);
592   ierr = CeedCalloc(P1d, &qweightdummy); CeedChk(ierr);
593   ierr = CeedBasisCreateTensorH1(ceed, dim, ncomp, P1d, P1d, x2, graddummy,
594                                  qrefdummy, qweightdummy, &fdm_basis);
595   CeedChk(ierr);
596   ierr = CeedFree(&graddummy); CeedChk(ierr);
597   ierr = CeedFree(&qrefdummy); CeedChk(ierr);
598   ierr = CeedFree(&qweightdummy); CeedChk(ierr);
599   ierr = CeedFree(&x2); CeedChk(ierr);
600   ierr = CeedFree(&lambda); CeedChk(ierr);
601 
602   // -- Restriction
603   CeedElemRestriction rstr_i;
604   ierr = CeedElemRestrictionCreateIdentity(ceed, nelem, nnodes, nnodes*nelem,
605          ncomp, &rstr_i); CeedChk(ierr);
606   // -- QFunction
607   CeedQFunction mass_qf;
608   ierr = CeedQFunctionCreateInteriorByName(ceed, "MassApply", &mass_qf);
609   CeedChk(ierr);
610   // -- Operator
611   ierr = CeedOperatorCreate(ceed, mass_qf, NULL, NULL, fdminv); CeedChk(ierr);
612   CeedOperatorSetField(*fdminv, "u", rstr_i, CEED_NOTRANSPOSE,
613                        fdm_basis, CEED_VECTOR_ACTIVE); CeedChk(ierr);
614   CeedOperatorSetField(*fdminv, "qdata", rstr_i, CEED_NOTRANSPOSE,
615                        CEED_BASIS_COLLOCATED, qdata); CeedChk(ierr);
616   CeedOperatorSetField(*fdminv, "v", rstr_i, CEED_NOTRANSPOSE,
617                        fdm_basis, CEED_VECTOR_ACTIVE); CeedChk(ierr);
618 
619   // Cleanup
620   ierr = CeedVectorDestroy(&qdata); CeedChk(ierr);
621   ierr = CeedBasisDestroy(&fdm_basis); CeedChk(ierr);
622   ierr = CeedElemRestrictionDestroy(&rstr_i); CeedChk(ierr);
623   ierr = CeedQFunctionDestroy(&mass_qf); CeedChk(ierr);
624 
625   return 0;
626 }
627 
628 
629 /**
630   @brief Apply CeedOperator to a vector
631 
632   This computes the action of the operator on the specified (active) input,
633   yielding its (active) output.  All inputs and outputs must be specified using
634   CeedOperatorSetField().
635 
636   @param op        CeedOperator to apply
637   @param[in] in    CeedVector containing input state or CEED_VECTOR_NONE if
638                   there are no active inputs
639   @param[out] out  CeedVector to store result of applying operator (must be
640                      distinct from @a in) or CEED_VECTOR_NONE if there are no
641                      active outputs
642   @param request   Address of CeedRequest for non-blocking completion, else
643                      CEED_REQUEST_IMMEDIATE
644 
645   @return An error code: 0 - success, otherwise - failure
646 
647   @ref Basic
648 **/
649 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
650                       CeedRequest *request) {
651   int ierr;
652   Ceed ceed = op->ceed;
653   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
654 
655   if (op->numelements)  {
656     // Standard Operator
657     if (op->Apply) {
658       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
659     } else {
660       // Zero all output vectors
661       CeedQFunction qf = op->qf;
662       for (CeedInt i=0; i<qf->numoutputfields; i++) {
663         CeedVector vec = op->outputfields[i]->vec;
664         if (vec == CEED_VECTOR_ACTIVE)
665           vec = out;
666         if (vec != CEED_VECTOR_NONE) {
667           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
668         }
669       }
670       // Apply
671       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
672     }
673   } else if (op->composite) {
674     // Composite Operator
675     if (op->ApplyComposite) {
676       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
677     } else {
678       CeedInt numsub;
679       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
680       CeedOperator *suboperators;
681       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
682 
683       // Zero all output vectors
684       if (out != CEED_VECTOR_NONE) {
685         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
686       }
687       for (CeedInt i=0; i<numsub; i++) {
688         for (CeedInt j=0; j<suboperators[i]->qf->numoutputfields; j++) {
689           CeedVector vec = suboperators[i]->outputfields[j]->vec;
690           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
691             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
692           }
693         }
694       }
695       // Apply
696       for (CeedInt i=0; i<op->numsub; i++) {
697         ierr = CeedOperatorApplyAdd(op->suboperators[i], in, out, request);
698         CeedChk(ierr);
699       }
700     }
701   }
702 
703   return 0;
704 }
705 
706 /**
707   @brief Apply CeedOperator to a vector and add result to output vector
708 
709   This computes the action of the operator on the specified (active) input,
710   yielding its (active) output.  All inputs and outputs must be specified using
711   CeedOperatorSetField().
712 
713   @param op        CeedOperator to apply
714   @param[in] in    CeedVector containing input state or NULL if there are no
715                      active inputs
716   @param[out] out  CeedVector to sum in result of applying operator (must be
717                      distinct from @a in) or NULL if there are no active outputs
718   @param request   Address of CeedRequest for non-blocking completion, else
719                      CEED_REQUEST_IMMEDIATE
720 
721   @return An error code: 0 - success, otherwise - failure
722 
723   @ref Basic
724 **/
725 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
726                          CeedRequest *request) {
727   int ierr;
728   Ceed ceed = op->ceed;
729   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
730 
731   if (op->numelements)  {
732     // Standard Operator
733     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
734   } else if (op->composite) {
735     // Composite Operator
736     if (op->ApplyAddComposite) {
737       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
738     } else {
739       CeedInt numsub;
740       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
741       CeedOperator *suboperators;
742       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
743 
744       for (CeedInt i=0; i<numsub; i++) {
745         ierr = CeedOperatorApplyAdd(suboperators[i], in, out, request);
746         CeedChk(ierr);
747       }
748     }
749   }
750 
751   return 0;
752 }
753 
754 /**
755   @brief Get the Ceed associated with a CeedOperator
756 
757   @param op              CeedOperator
758   @param[out] ceed       Variable to store Ceed
759 
760   @return An error code: 0 - success, otherwise - failure
761 
762   @ref Advanced
763 **/
764 
765 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
766   *ceed = op->ceed;
767   return 0;
768 }
769 
770 /**
771   @brief Get the number of elements associated with a CeedOperator
772 
773   @param op              CeedOperator
774   @param[out] numelem    Variable to store number of elements
775 
776   @return An error code: 0 - success, otherwise - failure
777 
778   @ref Advanced
779 **/
780 
781 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *numelem) {
782   if (op->composite)
783     // LCOV_EXCL_START
784     return CeedError(op->ceed, 1, "Not defined for composite operator");
785   // LCOV_EXCL_STOP
786 
787   *numelem = op->numelements;
788   return 0;
789 }
790 
791 /**
792   @brief Get the number of quadrature points associated with a CeedOperator
793 
794   @param op              CeedOperator
795   @param[out] numqpts    Variable to store vector number of quadrature points
796 
797   @return An error code: 0 - success, otherwise - failure
798 
799   @ref Advanced
800 **/
801 
802 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *numqpts) {
803   if (op->composite)
804     // LCOV_EXCL_START
805     return CeedError(op->ceed, 1, "Not defined for composite operator");
806   // LCOV_EXCL_STOP
807 
808   *numqpts = op->numqpoints;
809   return 0;
810 }
811 
812 /**
813   @brief Get the number of arguments associated with a CeedOperator
814 
815   @param op              CeedOperator
816   @param[out] numargs    Variable to store vector number of arguments
817 
818   @return An error code: 0 - success, otherwise - failure
819 
820   @ref Advanced
821 **/
822 
823 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *numargs) {
824   if (op->composite)
825     // LCOV_EXCL_START
826     return CeedError(op->ceed, 1, "Not defined for composite operators");
827   // LCOV_EXCL_STOP
828 
829   *numargs = op->nfields;
830   return 0;
831 }
832 
833 /**
834   @brief Get the setup status of a CeedOperator
835 
836   @param op             CeedOperator
837   @param[out] setupdone Variable to store setup status
838 
839   @return An error code: 0 - success, otherwise - failure
840 
841   @ref Advanced
842 **/
843 
844 int CeedOperatorGetSetupStatus(CeedOperator op, bool *setupdone) {
845   *setupdone = op->setupdone;
846   return 0;
847 }
848 
849 /**
850   @brief Get the QFunction associated with a CeedOperator
851 
852   @param op              CeedOperator
853   @param[out] qf         Variable to store QFunction
854 
855   @return An error code: 0 - success, otherwise - failure
856 
857   @ref Advanced
858 **/
859 
860 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
861   if (op->composite)
862     // LCOV_EXCL_START
863     return CeedError(op->ceed, 1, "Not defined for composite operator");
864   // LCOV_EXCL_STOP
865 
866   *qf = op->qf;
867   return 0;
868 }
869 
870 /**
871   @brief Get the number of suboperators associated with a CeedOperator
872 
873   @param op              CeedOperator
874   @param[out] numsub     Variable to store number of suboperators
875 
876   @return An error code: 0 - success, otherwise - failure
877 
878   @ref Advanced
879 **/
880 
881 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *numsub) {
882   if (!op->composite)
883     // LCOV_EXCL_START
884     return CeedError(op->ceed, 1, "Not a composite operator");
885   // LCOV_EXCL_STOP
886 
887   *numsub = op->numsub;
888   return 0;
889 }
890 
891 /**
892   @brief Get the list of suboperators associated with a CeedOperator
893 
894   @param op                CeedOperator
895   @param[out] suboperators Variable to store list of suboperators
896 
897   @return An error code: 0 - success, otherwise - failure
898 
899   @ref Advanced
900 **/
901 
902 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **suboperators) {
903   if (!op->composite)
904     // LCOV_EXCL_START
905     return CeedError(op->ceed, 1, "Not a composite operator");
906   // LCOV_EXCL_STOP
907 
908   *suboperators = op->suboperators;
909   return 0;
910 }
911 
912 /**
913   @brief Set the backend data of a CeedOperator
914 
915   @param[out] op         CeedOperator
916   @param data            Data to set
917 
918   @return An error code: 0 - success, otherwise - failure
919 
920   @ref Advanced
921 **/
922 
923 int CeedOperatorSetData(CeedOperator op, void **data) {
924   op->data = *data;
925   return 0;
926 }
927 
928 /**
929   @brief Get the backend data of a CeedOperator
930 
931   @param op              CeedOperator
932   @param[out] data       Variable to store data
933 
934   @return An error code: 0 - success, otherwise - failure
935 
936   @ref Advanced
937 **/
938 
939 int CeedOperatorGetData(CeedOperator op, void **data) {
940   *data = op->data;
941   return 0;
942 }
943 
944 /**
945   @brief Set the setup flag of a CeedOperator to True
946 
947   @param op              CeedOperator
948 
949   @return An error code: 0 - success, otherwise - failure
950 
951   @ref Advanced
952 **/
953 
954 int CeedOperatorSetSetupDone(CeedOperator op) {
955   op->setupdone = 1;
956   return 0;
957 }
958 
959 /**
960   @brief View a field of a CeedOperator
961 
962   @param[in] field       Operator field to view
963   @param[in] fieldnumber Number of field being viewed
964   @param[in] stream      Stream to view to, e.g., stdout
965 
966   @return An error code: 0 - success, otherwise - failure
967 
968   @ref Utility
969 **/
970 static int CeedOperatorFieldView(CeedOperatorField field,
971                                  CeedQFunctionField qffield,
972                                  CeedInt fieldnumber, bool sub, bool in,
973                                  FILE *stream) {
974   const char *pre = sub ? "  " : "";
975   const char *inout = in ? "Input" : "Output";
976 
977   fprintf(stream, "%s    %s Field [%d]:\n"
978           "%s      Name: \"%s\"\n"
979           "%s      Lmode: \"%s\"\n",
980           pre, inout, fieldnumber, pre, qffield->fieldname,
981           pre, CeedTransposeModes[field->lmode]);
982 
983   if (field->basis == CEED_BASIS_COLLOCATED)
984     fprintf(stream, "%s      Collocated basis\n", pre);
985 
986   if (field->vec == CEED_VECTOR_ACTIVE)
987     fprintf(stream, "%s      Active vector\n", pre);
988   else if (field->vec == CEED_VECTOR_NONE)
989     fprintf(stream, "%s      No vector\n", pre);
990 
991   return 0;
992 }
993 
994 /**
995   @brief View a single CeedOperator
996 
997   @param[in] op     CeedOperator to view
998   @param[in] stream Stream to write; typically stdout/stderr or a file
999 
1000   @return Error code: 0 - success, otherwise - failure
1001 
1002   @ref Utility
1003 **/
1004 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
1005   int ierr;
1006   const char *pre = sub ? "  " : "";
1007 
1008   CeedInt totalfields;
1009   ierr = CeedOperatorGetNumArgs(op, &totalfields); CeedChk(ierr);
1010 
1011   fprintf(stream, "%s  %d Field%s\n", pre, totalfields, totalfields>1 ? "s" : "");
1012 
1013   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->numinputfields,
1014           op->qf->numinputfields>1 ? "s" : "");
1015   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
1016     ierr = CeedOperatorFieldView(op->inputfields[i], op->qf->inputfields[i],
1017                                  i, sub, 1, stream); CeedChk(ierr);
1018   }
1019 
1020   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->numoutputfields,
1021           op->qf->numoutputfields>1 ? "s" : "");
1022   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
1023     ierr = CeedOperatorFieldView(op->outputfields[i], op->qf->inputfields[i],
1024                                  i, sub, 0, stream); CeedChk(ierr);
1025   }
1026 
1027   return 0;
1028 }
1029 
1030 /**
1031   @brief View a CeedOperator
1032 
1033   @param[in] op     CeedOperator to view
1034   @param[in] stream Stream to write; typically stdout/stderr or a file
1035 
1036   @return Error code: 0 - success, otherwise - failure
1037 
1038   @ref Utility
1039 **/
1040 int CeedOperatorView(CeedOperator op, FILE *stream) {
1041   int ierr;
1042 
1043   if (op->composite) {
1044     fprintf(stream, "Composite CeedOperator\n");
1045 
1046     for (CeedInt i=0; i<op->numsub; i++) {
1047       fprintf(stream, "  SubOperator [%d]:\n", i);
1048       ierr = CeedOperatorSingleView(op->suboperators[i], 1, stream);
1049       CeedChk(ierr);
1050     }
1051   } else {
1052     fprintf(stream, "CeedOperator\n");
1053     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1054   }
1055 
1056   return 0;
1057 }
1058 
1059 /**
1060   @brief Get the CeedOperatorFields of a CeedOperator
1061 
1062   @param op                 CeedOperator
1063   @param[out] inputfields   Variable to store inputfields
1064   @param[out] outputfields  Variable to store outputfields
1065 
1066   @return An error code: 0 - success, otherwise - failure
1067 
1068   @ref Advanced
1069 **/
1070 
1071 int CeedOperatorGetFields(CeedOperator op, CeedOperatorField **inputfields,
1072                           CeedOperatorField **outputfields) {
1073   if (op->composite)
1074     // LCOV_EXCL_START
1075     return CeedError(op->ceed, 1, "Not defined for composite operator");
1076   // LCOV_EXCL_STOP
1077 
1078   if (inputfields) *inputfields = op->inputfields;
1079   if (outputfields) *outputfields = op->outputfields;
1080   return 0;
1081 }
1082 
1083 /**
1084   @brief Get the L vector CeedTransposeMode of a CeedOperatorField
1085 
1086   @param opfield         CeedOperatorField
1087   @param[out] lmode      Variable to store CeedTransposeMode
1088 
1089   @return An error code: 0 - success, otherwise - failure
1090 
1091   @ref Advanced
1092 **/
1093 
1094 int CeedOperatorFieldGetLMode(CeedOperatorField opfield,
1095                               CeedTransposeMode *lmode) {
1096   *lmode = opfield->lmode;
1097   return 0;
1098 }
1099 
1100 /**
1101   @brief Get the CeedElemRestriction of a CeedOperatorField
1102 
1103   @param opfield         CeedOperatorField
1104   @param[out] rstr       Variable to store CeedElemRestriction
1105 
1106   @return An error code: 0 - success, otherwise - failure
1107 
1108   @ref Advanced
1109 **/
1110 
1111 int CeedOperatorFieldGetElemRestriction(CeedOperatorField opfield,
1112                                         CeedElemRestriction *rstr) {
1113   *rstr = opfield->Erestrict;
1114   return 0;
1115 }
1116 
1117 /**
1118   @brief Get the CeedBasis of a CeedOperatorField
1119 
1120   @param opfield         CeedOperatorField
1121   @param[out] basis      Variable to store CeedBasis
1122 
1123   @return An error code: 0 - success, otherwise - failure
1124 
1125   @ref Advanced
1126 **/
1127 
1128 int CeedOperatorFieldGetBasis(CeedOperatorField opfield, CeedBasis *basis) {
1129   *basis = opfield->basis;
1130   return 0;
1131 }
1132 
1133 /**
1134   @brief Get the CeedVector of a CeedOperatorField
1135 
1136   @param opfield         CeedOperatorField
1137   @param[out] vec        Variable to store CeedVector
1138 
1139   @return An error code: 0 - success, otherwise - failure
1140 
1141   @ref Advanced
1142 **/
1143 
1144 int CeedOperatorFieldGetVector(CeedOperatorField opfield, CeedVector *vec) {
1145   *vec = opfield->vec;
1146   return 0;
1147 }
1148 
1149 /**
1150   @brief Destroy a CeedOperator
1151 
1152   @param op CeedOperator to destroy
1153 
1154   @return An error code: 0 - success, otherwise - failure
1155 
1156   @ref Basic
1157 **/
1158 int CeedOperatorDestroy(CeedOperator *op) {
1159   int ierr;
1160 
1161   if (!*op || --(*op)->refcount > 0) return 0;
1162   if ((*op)->Destroy) {
1163     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1164   }
1165   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1166   // Free fields
1167   for (int i=0; i<(*op)->nfields; i++)
1168     if ((*op)->inputfields[i]) {
1169       ierr = CeedElemRestrictionDestroy(&(*op)->inputfields[i]->Erestrict);
1170       CeedChk(ierr);
1171       if ((*op)->inputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1172         ierr = CeedBasisDestroy(&(*op)->inputfields[i]->basis); CeedChk(ierr);
1173       }
1174       if ((*op)->inputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1175           (*op)->inputfields[i]->vec != CEED_VECTOR_NONE ) {
1176         ierr = CeedVectorDestroy(&(*op)->inputfields[i]->vec); CeedChk(ierr);
1177       }
1178       ierr = CeedFree(&(*op)->inputfields[i]); CeedChk(ierr);
1179     }
1180   for (int i=0; i<(*op)->nfields; i++)
1181     if ((*op)->outputfields[i]) {
1182       ierr = CeedElemRestrictionDestroy(&(*op)->outputfields[i]->Erestrict);
1183       CeedChk(ierr);
1184       if ((*op)->outputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1185         ierr = CeedBasisDestroy(&(*op)->outputfields[i]->basis); CeedChk(ierr);
1186       }
1187       if ((*op)->outputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1188           (*op)->outputfields[i]->vec != CEED_VECTOR_NONE ) {
1189         ierr = CeedVectorDestroy(&(*op)->outputfields[i]->vec); CeedChk(ierr);
1190       }
1191       ierr = CeedFree(&(*op)->outputfields[i]); CeedChk(ierr);
1192     }
1193   // Destroy suboperators
1194   for (int i=0; i<(*op)->numsub; i++)
1195     if ((*op)->suboperators[i]) {
1196       ierr = CeedOperatorDestroy(&(*op)->suboperators[i]); CeedChk(ierr);
1197     }
1198   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1199   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1200   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1201 
1202   // Destroy fallback
1203   if ((*op)->opfallback) {
1204     ierr = (*op)->qffallback->Destroy((*op)->qffallback); CeedChk(ierr);
1205     ierr = CeedFree(&(*op)->qffallback); CeedChk(ierr);
1206     ierr = (*op)->opfallback->Destroy((*op)->opfallback); CeedChk(ierr);
1207     ierr = CeedFree(&(*op)->opfallback); CeedChk(ierr);
1208   }
1209 
1210   ierr = CeedFree(&(*op)->inputfields); CeedChk(ierr);
1211   ierr = CeedFree(&(*op)->outputfields); CeedChk(ierr);
1212   ierr = CeedFree(&(*op)->suboperators); CeedChk(ierr);
1213   ierr = CeedFree(op); CeedChk(ierr);
1214   return 0;
1215 }
1216 
1217 /// @}
1218