xref: /libCEED/interface/ceed-operator.c (revision 1d102b48beba01a4e67ef28409a80cc546ae2651)
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   (*op)->qf = qf;
66   qf->refcount++;
67   (*op)->dqf = dqf;
68   if (dqf) dqf->refcount++;
69   (*op)->dqfT = dqfT;
70   if (dqfT) dqfT->refcount++;
71   ierr = CeedCalloc(16, &(*op)->inputfields); CeedChk(ierr);
72   ierr = CeedCalloc(16, &(*op)->outputfields); CeedChk(ierr);
73   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
74   return 0;
75 }
76 
77 /**
78   @brief Create an operator that composes the action of several operators
79 
80   @param ceed    A Ceed object where the CeedOperator will be created
81   @param[out] op Address of the variable where the newly created
82                      Composite CeedOperator will be stored
83 
84   @return An error code: 0 - success, otherwise - failure
85 
86   @ref Basic
87  */
88 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
89   int ierr;
90 
91   if (!ceed->CompositeOperatorCreate) {
92     Ceed delegate;
93     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
94 
95     if (!delegate)
96       // LCOV_EXCL_START
97       return CeedError(ceed, 1, "Backend does not support "
98                        "CompositeOperatorCreate");
99     // LCOV_EXCL_STOP
100 
101     ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
102     return 0;
103   }
104 
105   ierr = CeedCalloc(1,op); CeedChk(ierr);
106   (*op)->ceed = ceed;
107   ceed->refcount++;
108   (*op)->composite = true;
109   ierr = CeedCalloc(16, &(*op)->suboperators); CeedChk(ierr);
110   ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
111   return 0;
112 }
113 
114 /**
115   @brief Provide a field to a CeedOperator for use by its CeedQFunction
116 
117   This function is used to specify both active and passive fields to a
118   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
119   fields can inputs or outputs (updated in-place when operator is applied).
120 
121   Active fields must be specified using this function, but their data (in a
122   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
123   input and at most one active output.
124 
125   @param op         CeedOperator on which to provide the field
126   @param fieldname  Name of the field (to be matched with the name used by
127                       CeedQFunction)
128   @param r          CeedElemRestriction
129   @param lmode      CeedTransposeMode which specifies the ordering of the
130                       components of the l-vector used by this CeedOperatorField,
131                       CEED_NOTRANSPOSE indicates the component is the
132                       outermost index and CEED_TRANSPOSE indicates the component
133                       is the innermost index in ordering of the l-vector
134   @param b          CeedBasis in which the field resides or CEED_BASIS_COLLOCATED
135                       if collocated with quadrature points
136   @param v          CeedVector to be used by CeedOperator or CEED_VECTOR_ACTIVE
137                       if field is active or CEED_VECTOR_NONE if using
138                       CEED_EVAL_WEIGHT in the QFunction
139 
140   @return An error code: 0 - success, otherwise - failure
141 
142   @ref Basic
143 **/
144 int CeedOperatorSetField(CeedOperator op, const char *fieldname,
145                          CeedElemRestriction r, CeedTransposeMode lmode,
146                          CeedBasis b, CeedVector v) {
147   int ierr;
148   if (op->composite)
149     // LCOV_EXCL_START
150     return CeedError(op->ceed, 1, "Cannot add field to composite operator.");
151   // LCOV_EXCL_STOP
152   if (!r)
153     // LCOV_EXCL_START
154     return CeedError(op->ceed, 1,
155                      "ElemRestriction r for field \"%s\" must be non-NULL.",
156                      fieldname);
157   // LCOV_EXCL_STOP
158   if (!b)
159     // LCOV_EXCL_START
160     return CeedError(op->ceed, 1, "Basis b for field \"%s\" must be non-NULL.",
161                      fieldname);
162   // LCOV_EXCL_STOP
163   if (!v)
164     // LCOV_EXCL_START
165     return CeedError(op->ceed, 1, "Vector v for field \"%s\" must be non-NULL.",
166                      fieldname);
167   // LCOV_EXCL_STOP
168 
169   CeedInt numelements;
170   ierr = CeedElemRestrictionGetNumElements(r, &numelements); CeedChk(ierr);
171   if (op->numelements && op->numelements != numelements)
172     // LCOV_EXCL_START
173     return CeedError(op->ceed, 1,
174                      "ElemRestriction with %d elements incompatible with prior "
175                      "%d elements", numelements, op->numelements);
176   // LCOV_EXCL_STOP
177   op->numelements = numelements;
178 
179   if (b != CEED_BASIS_COLLOCATED) {
180     CeedInt numqpoints;
181     ierr = CeedBasisGetNumQuadraturePoints(b, &numqpoints); CeedChk(ierr);
182     if (op->numqpoints && op->numqpoints != numqpoints)
183       // LCOV_EXCL_START
184       return CeedError(op->ceed, 1, "Basis with %d quadrature points "
185                        "incompatible with prior %d points", numqpoints,
186                        op->numqpoints);
187     // LCOV_EXCL_STOP
188     op->numqpoints = numqpoints;
189   }
190   CeedOperatorField *ofield;
191   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
192     if (!strcmp(fieldname, (*op->qf->inputfields[i]).fieldname)) {
193       ofield = &op->inputfields[i];
194       goto found;
195     }
196   }
197   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
198     if (!strcmp(fieldname, (*op->qf->outputfields[i]).fieldname)) {
199       ofield = &op->outputfields[i];
200       goto found;
201     }
202   }
203   // LCOV_EXCL_START
204   return CeedError(op->ceed, 1, "QFunction has no knowledge of field '%s'",
205                    fieldname);
206   // LCOV_EXCL_STOP
207 found:
208   ierr = CeedCalloc(1, ofield); CeedChk(ierr);
209   (*ofield)->Erestrict = r;
210   r->refcount += 1;
211   (*ofield)->lmode = lmode;
212   (*ofield)->basis = b;
213   if (b != CEED_BASIS_COLLOCATED)
214     b->refcount += 1;
215   (*ofield)->vec = v;
216   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE)
217     v->refcount += 1;
218   op->nfields += 1;
219   return 0;
220 }
221 
222 /**
223   @brief Add a sub-operator to a composite CeedOperator
224 
225   @param[out] compositeop Address of the composite CeedOperator
226   @param      subop       Address of the sub-operator CeedOperator
227 
228   @return An error code: 0 - success, otherwise - failure
229 
230   @ref Basic
231  */
232 int CeedCompositeOperatorAddSub(CeedOperator compositeop,
233                                 CeedOperator subop) {
234   if (!compositeop->composite)
235     // LCOV_EXCL_START
236     return CeedError(compositeop->ceed, 1, "CeedOperator is not a composite "
237                      "operator");
238   // LCOV_EXCL_STOP
239 
240   if (compositeop->numsub == CEED_COMPOSITE_MAX)
241     // LCOV_EXCL_START
242     return CeedError(compositeop->ceed, 1, "Cannot add additional suboperators");
243   // LCOV_EXCL_STOP
244 
245   compositeop->suboperators[compositeop->numsub] = subop;
246   subop->refcount++;
247   compositeop->numsub++;
248   return 0;
249 }
250 
251 /**
252   @brief Assemble a linear CeedQFunction associated with a CeedOperator
253 
254   This returns a CeedVector containing a matrix at each quadrature point
255     providing the action of the CeedQFunction associated with the CeedOperator.
256     The vector 'assembled' is of shape
257       [num_elements, num_input_fields, num_output_fields, num_quad_points]
258     and contains column-major matrices representing the action of the
259     CeedQFunction for a corresponding quadrature point on an element. Inputs and
260     outputs are in the order provided by the user when adding CeedOperator fields.
261     For example, a QFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
262     'v', provided in that order, would result in an assembled QFunction that
263     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
264     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
265 
266   @param op             CeedOperator to assemble CeedQFunction
267   @param[out] assembled CeedVector to store assembled Ceed QFunction at
268                           quadrature points
269   @param[out] rstr      CeedElemRestriction for CeedVector containing assembled
270                           CeedQFunction
271   @param request        Address of CeedRequest for non-blocking completion, else
272                           CEED_REQUEST_IMMEDIATE
273 
274   @return An error code: 0 - success, otherwise - failure
275 
276   @ref Advanced
277 **/
278 int CeedOperatorAssembleLinearQFunction(CeedOperator op, CeedVector *assembled,
279     CeedElemRestriction *rstr, CeedRequest *request) {
280   int ierr;
281   Ceed ceed = op->ceed;
282   CeedQFunction qf = op->qf;
283 
284   if (op->composite) {
285     return CeedError(ceed, 1, "Cannot assemble QFunction for composite operator");
286   } else {
287     if (op->nfields == 0)
288       return CeedError(ceed, 1, "No operator fields set");
289     if (op->nfields < qf->numinputfields + qf->numoutputfields)
290       return CeedError( ceed, 1, "Not all operator fields set");
291     if (op->numelements == 0)
292       return CeedError(ceed, 1, "At least one restriction required");
293     if (op->numqpoints == 0)
294       return CeedError(ceed, 1, "At least one non-collocated basis required");
295   }
296   ierr = op->AssembleLinearQFunction(op, assembled, rstr, request);
297   CeedChk(ierr);
298   return 0;
299 }
300 
301 /**
302   @brief Apply CeedOperator to a vector
303 
304   This computes the action of the operator on the specified (active) input,
305   yielding its (active) output.  All inputs and outputs must be specified using
306   CeedOperatorSetField().
307 
308   @param op        CeedOperator to apply
309   @param[in] in    CeedVector containing input state or NULL if there are no
310                      active inputs
311   @param[out] out  CeedVector to store result of applying operator (must be
312                      distinct from @a in) or NULL if there are no active outputs
313   @param request   Address of CeedRequest for non-blocking completion, else
314                      CEED_REQUEST_IMMEDIATE
315 
316   @return An error code: 0 - success, otherwise - failure
317 
318   @ref Basic
319 **/
320 int CeedOperatorApply(CeedOperator op, CeedVector in,
321                       CeedVector out, CeedRequest *request) {
322   int ierr;
323   Ceed ceed = op->ceed;
324   CeedQFunction qf = op->qf;
325 
326   if (op->composite) {
327     if (!op->numsub)
328       // LCOV_EXCL_START
329       return CeedError(ceed, 1, "No suboperators set");
330     // LCOV_EXCL_STOP
331   } else {
332     if (op->nfields == 0)
333       // LCOV_EXCL_START
334       return CeedError(ceed, 1, "No operator fields set");
335     // LCOV_EXCL_STOP
336     if (op->nfields < qf->numinputfields + qf->numoutputfields)
337       // LCOV_EXCL_START
338       return CeedError(ceed, 1, "Not all operator fields set");
339     // LCOV_EXCL_STOP
340     if (op->numelements == 0)
341       // LCOV_EXCL_START
342       return CeedError(ceed, 1,"At least one restriction required");
343     // LCOV_EXCL_STOP
344     if (op->numqpoints == 0)
345       // LCOV_EXCL_START
346       return CeedError(ceed, 1,"At least one non-collocated basis required");
347     // LCOV_EXCL_STOP
348   }
349   ierr = op->Apply(op, in, out, request); CeedChk(ierr);
350   return 0;
351 }
352 
353 /**
354   @brief Get the Ceed associated with a CeedOperator
355 
356   @param op              CeedOperator
357   @param[out] ceed       Variable to store Ceed
358 
359   @return An error code: 0 - success, otherwise - failure
360 
361   @ref Advanced
362 **/
363 
364 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
365   *ceed = op->ceed;
366   return 0;
367 }
368 
369 /**
370   @brief Get the number of elements associated with a CeedOperator
371 
372   @param op              CeedOperator
373   @param[out] numelem    Variable to store number of elements
374 
375   @return An error code: 0 - success, otherwise - failure
376 
377   @ref Advanced
378 **/
379 
380 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *numelem) {
381   if (op->composite)
382     // LCOV_EXCL_START
383     return CeedError(op->ceed, 1, "Not defined for composite operator");
384   // LCOV_EXCL_STOP
385 
386   *numelem = op->numelements;
387   return 0;
388 }
389 
390 /**
391   @brief Get the number of quadrature points associated with a CeedOperator
392 
393   @param op              CeedOperator
394   @param[out] numqpts    Variable to store vector number of quadrature points
395 
396   @return An error code: 0 - success, otherwise - failure
397 
398   @ref Advanced
399 **/
400 
401 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *numqpts) {
402   if (op->composite)
403     // LCOV_EXCL_START
404     return CeedError(op->ceed, 1, "Not defined for composite operator");
405   // LCOV_EXCL_STOP
406 
407   *numqpts = op->numqpoints;
408   return 0;
409 }
410 
411 /**
412   @brief Get the number of arguments associated with a CeedOperator
413 
414   @param op              CeedOperator
415   @param[out] numargs    Variable to store vector number of arguments
416 
417   @return An error code: 0 - success, otherwise - failure
418 
419   @ref Advanced
420 **/
421 
422 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *numargs) {
423   if (op->composite)
424     // LCOV_EXCL_START
425     return CeedError(op->ceed, 1, "Not defined for composite operators");
426   // LCOV_EXCL_STOP
427 
428   *numargs = op->nfields;
429   return 0;
430 }
431 
432 /**
433   @brief Get the setup status of a CeedOperator
434 
435   @param op             CeedOperator
436   @param[out] setupdone Variable to store setup status
437 
438   @return An error code: 0 - success, otherwise - failure
439 
440   @ref Advanced
441 **/
442 
443 int CeedOperatorGetSetupStatus(CeedOperator op, bool *setupdone) {
444   *setupdone = op->setupdone;
445   return 0;
446 }
447 
448 /**
449   @brief Get the QFunction associated with a CeedOperator
450 
451   @param op              CeedOperator
452   @param[out] qf         Variable to store QFunction
453 
454   @return An error code: 0 - success, otherwise - failure
455 
456   @ref Advanced
457 **/
458 
459 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
460   if (op->composite)
461     // LCOV_EXCL_START
462     return CeedError(op->ceed, 1, "Not defined for composite operator");
463   // LCOV_EXCL_STOP
464 
465   *qf = op->qf;
466   return 0;
467 }
468 
469 /**
470   @brief Get the number of suboperators associated with a CeedOperator
471 
472   @param op              CeedOperator
473   @param[out] numsub     Variable to store number of suboperators
474 
475   @return An error code: 0 - success, otherwise - failure
476 
477   @ref Advanced
478 **/
479 
480 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *numsub) {
481   if (!op->composite)
482     // LCOV_EXCL_START
483     return CeedError(op->ceed, 1, "Not a composite operator");
484   // LCOV_EXCL_STOP
485 
486   *numsub = op->numsub;
487   return 0;
488 }
489 
490 /**
491   @brief Get the list of suboperators associated with a CeedOperator
492 
493   @param op                CeedOperator
494   @param[out] suboperators Variable to store list of suboperators
495 
496   @return An error code: 0 - success, otherwise - failure
497 
498   @ref Advanced
499 **/
500 
501 int CeedOperatorGetSubList(CeedOperator op, CeedOperator* *suboperators) {
502   if (!op->composite)
503     // LCOV_EXCL_START
504     return CeedError(op->ceed, 1, "Not a composite operator");
505   // LCOV_EXCL_STOP
506 
507   *suboperators = op->suboperators;
508   return 0;
509 }
510 
511 /**
512   @brief Set the backend data of a CeedOperator
513 
514   @param[out] op         CeedOperator
515   @param data            Data to set
516 
517   @return An error code: 0 - success, otherwise - failure
518 
519   @ref Advanced
520 **/
521 
522 int CeedOperatorSetData(CeedOperator op, void* *data) {
523   op->data = *data;
524   return 0;
525 }
526 
527 /**
528   @brief Get the backend data of a CeedOperator
529 
530   @param op              CeedOperator
531   @param[out] data       Variable to store data
532 
533   @return An error code: 0 - success, otherwise - failure
534 
535   @ref Advanced
536 **/
537 
538 int CeedOperatorGetData(CeedOperator op, void* *data) {
539   *data = op->data;
540   return 0;
541 }
542 
543 /**
544   @brief Set the setup flag of a CeedOperator to True
545 
546   @param op              CeedOperator
547 
548   @return An error code: 0 - success, otherwise - failure
549 
550   @ref Advanced
551 **/
552 
553 int CeedOperatorSetSetupDone(CeedOperator op) {
554   op->setupdone = 1;
555   return 0;
556 }
557 
558 /**
559   @brief Get the CeedOperatorFields of a CeedOperator
560 
561   @param op                 CeedOperator
562   @param[out] inputfields   Variable to store inputfields
563   @param[out] outputfields  Variable to store outputfields
564 
565   @return An error code: 0 - success, otherwise - failure
566 
567   @ref Advanced
568 **/
569 
570 int CeedOperatorGetFields(CeedOperator op,
571                           CeedOperatorField* *inputfields,
572                           CeedOperatorField* *outputfields) {
573   if (op->composite)
574     // LCOV_EXCL_START
575     return CeedError(op->ceed, 1, "Not defined for composite operator");
576   // LCOV_EXCL_STOP
577 
578   if (inputfields) *inputfields = op->inputfields;
579   if (outputfields) *outputfields = op->outputfields;
580   return 0;
581 }
582 
583 /**
584   @brief Get the L vector CeedTransposeMode of a CeedOperatorField
585 
586   @param opfield         CeedOperatorField
587   @param[out] lmode      Variable to store CeedTransposeMode
588 
589   @return An error code: 0 - success, otherwise - failure
590 
591   @ref Advanced
592 **/
593 
594 int CeedOperatorFieldGetLMode(CeedOperatorField opfield,
595                               CeedTransposeMode *lmode) {
596   *lmode = opfield->lmode;
597   return 0;
598 }
599 
600 /**
601   @brief Get the CeedElemRestriction of a CeedOperatorField
602 
603   @param opfield         CeedOperatorField
604   @param[out] rstr       Variable to store CeedElemRestriction
605 
606   @return An error code: 0 - success, otherwise - failure
607 
608   @ref Advanced
609 **/
610 
611 int CeedOperatorFieldGetElemRestriction(CeedOperatorField opfield,
612                                         CeedElemRestriction *rstr) {
613   *rstr = opfield->Erestrict;
614   return 0;
615 }
616 
617 /**
618   @brief Get the CeedBasis of a CeedOperatorField
619 
620   @param opfield         CeedOperatorField
621   @param[out] basis      Variable to store CeedBasis
622 
623   @return An error code: 0 - success, otherwise - failure
624 
625   @ref Advanced
626 **/
627 
628 int CeedOperatorFieldGetBasis(CeedOperatorField opfield,
629                               CeedBasis *basis) {
630   *basis = opfield->basis;
631   return 0;
632 }
633 
634 /**
635   @brief Get the CeedVector of a CeedOperatorField
636 
637   @param opfield         CeedOperatorField
638   @param[out] vec        Variable to store CeedVector
639 
640   @return An error code: 0 - success, otherwise - failure
641 
642   @ref Advanced
643 **/
644 
645 int CeedOperatorFieldGetVector(CeedOperatorField opfield,
646                                CeedVector *vec) {
647   *vec = opfield->vec;
648   return 0;
649 }
650 
651 /**
652   @brief Destroy a CeedOperator
653 
654   @param op CeedOperator to destroy
655 
656   @return An error code: 0 - success, otherwise - failure
657 
658   @ref Basic
659 **/
660 int CeedOperatorDestroy(CeedOperator *op) {
661   int ierr;
662 
663   if (!*op || --(*op)->refcount > 0) return 0;
664   if ((*op)->Destroy) {
665     ierr = (*op)->Destroy(*op); CeedChk(ierr);
666   }
667   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
668   // Free fields
669   for (int i=0; i<(*op)->nfields; i++)
670     if ((*op)->inputfields[i]) {
671       ierr = CeedElemRestrictionDestroy(&(*op)->inputfields[i]->Erestrict);
672       CeedChk(ierr);
673       if ((*op)->inputfields[i]->basis != CEED_BASIS_COLLOCATED) {
674         ierr = CeedBasisDestroy(&(*op)->inputfields[i]->basis); CeedChk(ierr);
675       }
676       if ((*op)->inputfields[i]->vec != CEED_VECTOR_ACTIVE &&
677           (*op)->inputfields[i]->vec != CEED_VECTOR_NONE ) {
678         ierr = CeedVectorDestroy(&(*op)->inputfields[i]->vec); CeedChk(ierr);
679       }
680       ierr = CeedFree(&(*op)->inputfields[i]); CeedChk(ierr);
681     }
682   for (int i=0; i<(*op)->nfields; i++)
683     if ((*op)->outputfields[i]) {
684       ierr = CeedElemRestrictionDestroy(&(*op)->outputfields[i]->Erestrict);
685       CeedChk(ierr);
686       if ((*op)->outputfields[i]->basis != CEED_BASIS_COLLOCATED) {
687         ierr = CeedBasisDestroy(&(*op)->outputfields[i]->basis); CeedChk(ierr);
688       }
689       if ((*op)->outputfields[i]->vec != CEED_VECTOR_ACTIVE &&
690           (*op)->outputfields[i]->vec != CEED_VECTOR_NONE ) {
691         ierr = CeedVectorDestroy(&(*op)->outputfields[i]->vec); CeedChk(ierr);
692       }
693       ierr = CeedFree(&(*op)->outputfields[i]); CeedChk(ierr);
694     }
695   // Destroy suboperators
696   for (int i=0; i<(*op)->numsub; i++)
697     if ((*op)->suboperators[i]) {
698       ierr = CeedOperatorDestroy(&(*op)->suboperators[i]); CeedChk(ierr);
699     }
700   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
701   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
702   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
703 
704   ierr = CeedFree(&(*op)->inputfields); CeedChk(ierr);
705   ierr = CeedFree(&(*op)->outputfields); CeedChk(ierr);
706   ierr = CeedFree(&(*op)->suboperators); CeedChk(ierr);
707   ierr = CeedFree(op); CeedChk(ierr);
708   return 0;
709 }
710 
711 /// @}
712