xref: /libCEED/interface/ceed-operator.c (revision 138d40723aa3d9074a0e9bff3a6f097507ae8e2b)
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     // LCOV_EXCL_START
286     return CeedError(ceed, 1, "Cannot assemble QFunction for composite operator");
287     // LCOV_EXCL_STOP
288   } else {
289     if (op->nfields == 0)
290       // LCOV_EXCL_START
291       return CeedError(ceed, 1, "No operator fields set");
292       // LCOV_EXCL_STOP
293     if (op->nfields < qf->numinputfields + qf->numoutputfields)
294       // LCOV_EXCL_START
295       return CeedError( ceed, 1, "Not all operator fields set");
296     // LCOV_EXCL_STOP
297     if (op->numelements == 0)
298       // LCOV_EXCL_START
299       return CeedError(ceed, 1, "At least one restriction required");
300     // LCOV_EXCL_STOP
301     if (op->numqpoints == 0)
302       // LCOV_EXCL_START
303       return CeedError(ceed, 1, "At least one non-collocated basis required");
304     // LCOV_EXCL_STOP
305   }
306   ierr = op->AssembleLinearQFunction(op, assembled, rstr, request);
307   CeedChk(ierr);
308   return 0;
309 }
310 
311 /**
312   @brief Apply CeedOperator to a vector
313 
314   This computes the action of the operator on the specified (active) input,
315   yielding its (active) output.  All inputs and outputs must be specified using
316   CeedOperatorSetField().
317 
318   @param op        CeedOperator to apply
319   @param[in] in    CeedVector containing input state or NULL if there are no
320                      active inputs
321   @param[out] out  CeedVector to store result of applying operator (must be
322                      distinct from @a in) or NULL if there are no active outputs
323   @param request   Address of CeedRequest for non-blocking completion, else
324                      CEED_REQUEST_IMMEDIATE
325 
326   @return An error code: 0 - success, otherwise - failure
327 
328   @ref Basic
329 **/
330 int CeedOperatorApply(CeedOperator op, CeedVector in,
331                       CeedVector out, CeedRequest *request) {
332   int ierr;
333   Ceed ceed = op->ceed;
334   CeedQFunction qf = op->qf;
335 
336   if (op->composite) {
337     if (!op->numsub)
338       // LCOV_EXCL_START
339       return CeedError(ceed, 1, "No suboperators set");
340     // LCOV_EXCL_STOP
341   } else {
342     if (op->nfields == 0)
343       // LCOV_EXCL_START
344       return CeedError(ceed, 1, "No operator fields set");
345     // LCOV_EXCL_STOP
346     if (op->nfields < qf->numinputfields + qf->numoutputfields)
347       // LCOV_EXCL_START
348       return CeedError(ceed, 1, "Not all operator fields set");
349     // LCOV_EXCL_STOP
350     if (op->numelements == 0)
351       // LCOV_EXCL_START
352       return CeedError(ceed, 1,"At least one restriction required");
353     // LCOV_EXCL_STOP
354     if (op->numqpoints == 0)
355       // LCOV_EXCL_START
356       return CeedError(ceed, 1,"At least one non-collocated basis required");
357     // LCOV_EXCL_STOP
358   }
359   ierr = op->Apply(op, in, out, request); CeedChk(ierr);
360   return 0;
361 }
362 
363 /**
364   @brief Get the Ceed associated with a CeedOperator
365 
366   @param op              CeedOperator
367   @param[out] ceed       Variable to store Ceed
368 
369   @return An error code: 0 - success, otherwise - failure
370 
371   @ref Advanced
372 **/
373 
374 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
375   *ceed = op->ceed;
376   return 0;
377 }
378 
379 /**
380   @brief Get the number of elements associated with a CeedOperator
381 
382   @param op              CeedOperator
383   @param[out] numelem    Variable to store number of elements
384 
385   @return An error code: 0 - success, otherwise - failure
386 
387   @ref Advanced
388 **/
389 
390 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *numelem) {
391   if (op->composite)
392     // LCOV_EXCL_START
393     return CeedError(op->ceed, 1, "Not defined for composite operator");
394   // LCOV_EXCL_STOP
395 
396   *numelem = op->numelements;
397   return 0;
398 }
399 
400 /**
401   @brief Get the number of quadrature points associated with a CeedOperator
402 
403   @param op              CeedOperator
404   @param[out] numqpts    Variable to store vector number of quadrature points
405 
406   @return An error code: 0 - success, otherwise - failure
407 
408   @ref Advanced
409 **/
410 
411 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *numqpts) {
412   if (op->composite)
413     // LCOV_EXCL_START
414     return CeedError(op->ceed, 1, "Not defined for composite operator");
415   // LCOV_EXCL_STOP
416 
417   *numqpts = op->numqpoints;
418   return 0;
419 }
420 
421 /**
422   @brief Get the number of arguments associated with a CeedOperator
423 
424   @param op              CeedOperator
425   @param[out] numargs    Variable to store vector number of arguments
426 
427   @return An error code: 0 - success, otherwise - failure
428 
429   @ref Advanced
430 **/
431 
432 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *numargs) {
433   if (op->composite)
434     // LCOV_EXCL_START
435     return CeedError(op->ceed, 1, "Not defined for composite operators");
436   // LCOV_EXCL_STOP
437 
438   *numargs = op->nfields;
439   return 0;
440 }
441 
442 /**
443   @brief Get the setup status of a CeedOperator
444 
445   @param op             CeedOperator
446   @param[out] setupdone Variable to store setup status
447 
448   @return An error code: 0 - success, otherwise - failure
449 
450   @ref Advanced
451 **/
452 
453 int CeedOperatorGetSetupStatus(CeedOperator op, bool *setupdone) {
454   *setupdone = op->setupdone;
455   return 0;
456 }
457 
458 /**
459   @brief Get the QFunction associated with a CeedOperator
460 
461   @param op              CeedOperator
462   @param[out] qf         Variable to store QFunction
463 
464   @return An error code: 0 - success, otherwise - failure
465 
466   @ref Advanced
467 **/
468 
469 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
470   if (op->composite)
471     // LCOV_EXCL_START
472     return CeedError(op->ceed, 1, "Not defined for composite operator");
473   // LCOV_EXCL_STOP
474 
475   *qf = op->qf;
476   return 0;
477 }
478 
479 /**
480   @brief Get the number of suboperators associated with a CeedOperator
481 
482   @param op              CeedOperator
483   @param[out] numsub     Variable to store number of suboperators
484 
485   @return An error code: 0 - success, otherwise - failure
486 
487   @ref Advanced
488 **/
489 
490 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *numsub) {
491   if (!op->composite)
492     // LCOV_EXCL_START
493     return CeedError(op->ceed, 1, "Not a composite operator");
494   // LCOV_EXCL_STOP
495 
496   *numsub = op->numsub;
497   return 0;
498 }
499 
500 /**
501   @brief Get the list of suboperators associated with a CeedOperator
502 
503   @param op                CeedOperator
504   @param[out] suboperators Variable to store list of suboperators
505 
506   @return An error code: 0 - success, otherwise - failure
507 
508   @ref Advanced
509 **/
510 
511 int CeedOperatorGetSubList(CeedOperator op, CeedOperator* *suboperators) {
512   if (!op->composite)
513     // LCOV_EXCL_START
514     return CeedError(op->ceed, 1, "Not a composite operator");
515   // LCOV_EXCL_STOP
516 
517   *suboperators = op->suboperators;
518   return 0;
519 }
520 
521 /**
522   @brief Set the backend data of a CeedOperator
523 
524   @param[out] op         CeedOperator
525   @param data            Data to set
526 
527   @return An error code: 0 - success, otherwise - failure
528 
529   @ref Advanced
530 **/
531 
532 int CeedOperatorSetData(CeedOperator op, void* *data) {
533   op->data = *data;
534   return 0;
535 }
536 
537 /**
538   @brief Get the backend data of a CeedOperator
539 
540   @param op              CeedOperator
541   @param[out] data       Variable to store data
542 
543   @return An error code: 0 - success, otherwise - failure
544 
545   @ref Advanced
546 **/
547 
548 int CeedOperatorGetData(CeedOperator op, void* *data) {
549   *data = op->data;
550   return 0;
551 }
552 
553 /**
554   @brief Set the setup flag of a CeedOperator to True
555 
556   @param op              CeedOperator
557 
558   @return An error code: 0 - success, otherwise - failure
559 
560   @ref Advanced
561 **/
562 
563 int CeedOperatorSetSetupDone(CeedOperator op) {
564   op->setupdone = 1;
565   return 0;
566 }
567 
568 /**
569   @brief Get the CeedOperatorFields of a CeedOperator
570 
571   @param op                 CeedOperator
572   @param[out] inputfields   Variable to store inputfields
573   @param[out] outputfields  Variable to store outputfields
574 
575   @return An error code: 0 - success, otherwise - failure
576 
577   @ref Advanced
578 **/
579 
580 int CeedOperatorGetFields(CeedOperator op,
581                           CeedOperatorField* *inputfields,
582                           CeedOperatorField* *outputfields) {
583   if (op->composite)
584     // LCOV_EXCL_START
585     return CeedError(op->ceed, 1, "Not defined for composite operator");
586   // LCOV_EXCL_STOP
587 
588   if (inputfields) *inputfields = op->inputfields;
589   if (outputfields) *outputfields = op->outputfields;
590   return 0;
591 }
592 
593 /**
594   @brief Get the L vector CeedTransposeMode of a CeedOperatorField
595 
596   @param opfield         CeedOperatorField
597   @param[out] lmode      Variable to store CeedTransposeMode
598 
599   @return An error code: 0 - success, otherwise - failure
600 
601   @ref Advanced
602 **/
603 
604 int CeedOperatorFieldGetLMode(CeedOperatorField opfield,
605                               CeedTransposeMode *lmode) {
606   *lmode = opfield->lmode;
607   return 0;
608 }
609 
610 /**
611   @brief Get the CeedElemRestriction of a CeedOperatorField
612 
613   @param opfield         CeedOperatorField
614   @param[out] rstr       Variable to store CeedElemRestriction
615 
616   @return An error code: 0 - success, otherwise - failure
617 
618   @ref Advanced
619 **/
620 
621 int CeedOperatorFieldGetElemRestriction(CeedOperatorField opfield,
622                                         CeedElemRestriction *rstr) {
623   *rstr = opfield->Erestrict;
624   return 0;
625 }
626 
627 /**
628   @brief Get the CeedBasis of a CeedOperatorField
629 
630   @param opfield         CeedOperatorField
631   @param[out] basis      Variable to store CeedBasis
632 
633   @return An error code: 0 - success, otherwise - failure
634 
635   @ref Advanced
636 **/
637 
638 int CeedOperatorFieldGetBasis(CeedOperatorField opfield,
639                               CeedBasis *basis) {
640   *basis = opfield->basis;
641   return 0;
642 }
643 
644 /**
645   @brief Get the CeedVector of a CeedOperatorField
646 
647   @param opfield         CeedOperatorField
648   @param[out] vec        Variable to store CeedVector
649 
650   @return An error code: 0 - success, otherwise - failure
651 
652   @ref Advanced
653 **/
654 
655 int CeedOperatorFieldGetVector(CeedOperatorField opfield,
656                                CeedVector *vec) {
657   *vec = opfield->vec;
658   return 0;
659 }
660 
661 /**
662   @brief Destroy a CeedOperator
663 
664   @param op CeedOperator to destroy
665 
666   @return An error code: 0 - success, otherwise - failure
667 
668   @ref Basic
669 **/
670 int CeedOperatorDestroy(CeedOperator *op) {
671   int ierr;
672 
673   if (!*op || --(*op)->refcount > 0) return 0;
674   if ((*op)->Destroy) {
675     ierr = (*op)->Destroy(*op); CeedChk(ierr);
676   }
677   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
678   // Free fields
679   for (int i=0; i<(*op)->nfields; i++)
680     if ((*op)->inputfields[i]) {
681       ierr = CeedElemRestrictionDestroy(&(*op)->inputfields[i]->Erestrict);
682       CeedChk(ierr);
683       if ((*op)->inputfields[i]->basis != CEED_BASIS_COLLOCATED) {
684         ierr = CeedBasisDestroy(&(*op)->inputfields[i]->basis); CeedChk(ierr);
685       }
686       if ((*op)->inputfields[i]->vec != CEED_VECTOR_ACTIVE &&
687           (*op)->inputfields[i]->vec != CEED_VECTOR_NONE ) {
688         ierr = CeedVectorDestroy(&(*op)->inputfields[i]->vec); CeedChk(ierr);
689       }
690       ierr = CeedFree(&(*op)->inputfields[i]); CeedChk(ierr);
691     }
692   for (int i=0; i<(*op)->nfields; i++)
693     if ((*op)->outputfields[i]) {
694       ierr = CeedElemRestrictionDestroy(&(*op)->outputfields[i]->Erestrict);
695       CeedChk(ierr);
696       if ((*op)->outputfields[i]->basis != CEED_BASIS_COLLOCATED) {
697         ierr = CeedBasisDestroy(&(*op)->outputfields[i]->basis); CeedChk(ierr);
698       }
699       if ((*op)->outputfields[i]->vec != CEED_VECTOR_ACTIVE &&
700           (*op)->outputfields[i]->vec != CEED_VECTOR_NONE ) {
701         ierr = CeedVectorDestroy(&(*op)->outputfields[i]->vec); CeedChk(ierr);
702       }
703       ierr = CeedFree(&(*op)->outputfields[i]); CeedChk(ierr);
704     }
705   // Destroy suboperators
706   for (int i=0; i<(*op)->numsub; i++)
707     if ((*op)->suboperators[i]) {
708       ierr = CeedOperatorDestroy(&(*op)->suboperators[i]); CeedChk(ierr);
709     }
710   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
711   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
712   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
713 
714   ierr = CeedFree(&(*op)->inputfields); CeedChk(ierr);
715   ierr = CeedFree(&(*op)->outputfields); CeedChk(ierr);
716   ierr = CeedFree(&(*op)->suboperators); CeedChk(ierr);
717   ierr = CeedFree(op); CeedChk(ierr);
718   return 0;
719 }
720 
721 /// @}
722