xref: /libCEED/rust/libceed-sys/c-src/interface/ceed-operator.c (revision f74ec58466a7eebc696fd6b461de38faeedc8431)
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/ceed.h>
18 #include <ceed/backend.h>
19 #include <ceed-impl.h>
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <string.h>
23 
24 /// @file
25 /// Implementation of CeedOperator interfaces
26 
27 /// ----------------------------------------------------------------------------
28 /// CeedOperator Library Internal Functions
29 /// ----------------------------------------------------------------------------
30 /// @addtogroup CeedOperatorDeveloper
31 /// @{
32 
33 /**
34   @brief Check if a CeedOperator Field matches the QFunction Field
35 
36   @param[in] ceed      Ceed object for error handling
37   @param[in] qf_field  QFunction Field matching Operator Field
38   @param[in] r         Operator Field ElemRestriction
39   @param[in] b         Operator Field Basis
40 
41   @return An error code: 0 - success, otherwise - failure
42 
43   @ref Developer
44 **/
45 static int CeedOperatorCheckField(Ceed ceed, CeedQFunctionField qf_field,
46                                   CeedElemRestriction r, CeedBasis b) {
47   int ierr;
48   CeedEvalMode eval_mode = qf_field->eval_mode;
49   CeedInt dim = 1, num_comp = 1, restr_num_comp = 1, size = qf_field->size;
50   // Restriction
51   if (r != CEED_ELEMRESTRICTION_NONE) {
52     if (eval_mode == CEED_EVAL_WEIGHT) {
53       // LCOV_EXCL_START
54       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
55                        "CEED_ELEMRESTRICTION_NONE should be used "
56                        "for a field with eval mode CEED_EVAL_WEIGHT");
57       // LCOV_EXCL_STOP
58     }
59     ierr = CeedElemRestrictionGetNumComponents(r, &restr_num_comp);
60     CeedChk(ierr);
61   }
62   if ((r == CEED_ELEMRESTRICTION_NONE) != (eval_mode == CEED_EVAL_WEIGHT)) {
63     // LCOV_EXCL_START
64     return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
65                      "CEED_ELEMRESTRICTION_NONE and CEED_EVAL_WEIGHT "
66                      "must be used together.");
67     // LCOV_EXCL_STOP
68   }
69   // Basis
70   if (b != CEED_BASIS_COLLOCATED) {
71     if (eval_mode == CEED_EVAL_NONE)
72       // LCOV_EXCL_START
73       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
74                        "Field '%s' configured with CEED_EVAL_NONE must "
75                        "be used with CEED_BASIS_COLLOCATED",
76                        // LCOV_EXCL_STOP
77                        qf_field->field_name);
78     ierr = CeedBasisGetDimension(b, &dim); CeedChk(ierr);
79     ierr = CeedBasisGetNumComponents(b, &num_comp); CeedChk(ierr);
80     if (r != CEED_ELEMRESTRICTION_NONE && restr_num_comp != num_comp) {
81       // LCOV_EXCL_START
82       return CeedError(ceed, CEED_ERROR_DIMENSION,
83                        "Field '%s' of size %d and EvalMode %s: ElemRestriction "
84                        "has %d components, but Basis has %d components",
85                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
86                        restr_num_comp,
87                        num_comp);
88       // LCOV_EXCL_STOP
89     }
90   }
91   // Field size
92   switch(eval_mode) {
93   case CEED_EVAL_NONE:
94     if (size != restr_num_comp)
95       // LCOV_EXCL_START
96       return CeedError(ceed, CEED_ERROR_DIMENSION,
97                        "Field '%s' of size %d and EvalMode %s: ElemRestriction has %d components",
98                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
99                        restr_num_comp);
100     // LCOV_EXCL_STOP
101     break;
102   case CEED_EVAL_INTERP:
103     if (size != num_comp)
104       // LCOV_EXCL_START
105       return CeedError(ceed, CEED_ERROR_DIMENSION,
106                        "Field '%s' of size %d and EvalMode %s: ElemRestriction/Basis has %d components",
107                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
108                        num_comp);
109     // LCOV_EXCL_STOP
110     break;
111   case CEED_EVAL_GRAD:
112     if (size != num_comp * dim)
113       // LCOV_EXCL_START
114       return CeedError(ceed, CEED_ERROR_DIMENSION,
115                        "Field '%s' of size %d and EvalMode %s in %d dimensions: "
116                        "ElemRestriction/Basis has %d components",
117                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], dim,
118                        num_comp);
119     // LCOV_EXCL_STOP
120     break;
121   case CEED_EVAL_WEIGHT:
122     // No additional checks required
123     break;
124   case CEED_EVAL_DIV:
125     // Not implemented
126     break;
127   case CEED_EVAL_CURL:
128     // Not implemented
129     break;
130   }
131   return CEED_ERROR_SUCCESS;
132 }
133 
134 /**
135   @brief View a field of a CeedOperator
136 
137   @param[in] field         Operator field to view
138   @param[in] qf_field      QFunction field (carries field name)
139   @param[in] field_number  Number of field being viewed
140   @param[in] sub           true indicates sub-operator, which increases indentation; false for top-level operator
141   @param[in] input         true for an input field; false for output field
142   @param[in] stream        Stream to view to, e.g., stdout
143 
144   @return An error code: 0 - success, otherwise - failure
145 
146   @ref Utility
147 **/
148 static int CeedOperatorFieldView(CeedOperatorField field,
149                                  CeedQFunctionField qf_field,
150                                  CeedInt field_number, bool sub, bool input,
151                                  FILE *stream) {
152   const char *pre = sub ? "  " : "";
153   const char *in_out = input ? "Input" : "Output";
154 
155   fprintf(stream, "%s    %s Field [%d]:\n"
156           "%s      Name: \"%s\"\n",
157           pre, in_out, field_number, pre, qf_field->field_name);
158 
159   if (field->basis == CEED_BASIS_COLLOCATED)
160     fprintf(stream, "%s      Collocated basis\n", pre);
161 
162   if (field->vec == CEED_VECTOR_ACTIVE)
163     fprintf(stream, "%s      Active vector\n", pre);
164   else if (field->vec == CEED_VECTOR_NONE)
165     fprintf(stream, "%s      No vector\n", pre);
166   return CEED_ERROR_SUCCESS;
167 }
168 
169 /**
170   @brief View a single CeedOperator
171 
172   @param[in] op      CeedOperator to view
173   @param[in] sub     Boolean flag for sub-operator
174   @param[in] stream  Stream to write; typically stdout/stderr or a file
175 
176   @return Error code: 0 - success, otherwise - failure
177 
178   @ref Utility
179 **/
180 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
181   int ierr;
182   const char *pre = sub ? "  " : "";
183 
184   CeedInt total_fields = 0;
185   ierr = CeedOperatorGetNumArgs(op, &total_fields); CeedChk(ierr);
186 
187   fprintf(stream, "%s  %d Field%s\n", pre, total_fields,
188           total_fields>1 ? "s" : "");
189 
190   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->num_input_fields,
191           op->qf->num_input_fields>1 ? "s" : "");
192   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
193     ierr = CeedOperatorFieldView(op->input_fields[i], op->qf->input_fields[i],
194                                  i, sub, 1, stream); CeedChk(ierr);
195   }
196 
197   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->num_output_fields,
198           op->qf->num_output_fields>1 ? "s" : "");
199   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
200     ierr = CeedOperatorFieldView(op->output_fields[i], op->qf->output_fields[i],
201                                  i, sub, 0, stream); CeedChk(ierr);
202   }
203   return CEED_ERROR_SUCCESS;
204 }
205 
206 /**
207   @brief Find the active vector basis for a CeedOperator
208 
209   @param[in] op             CeedOperator to find active basis for
210   @param[out] active_basis  Basis for active input vector
211 
212   @return An error code: 0 - success, otherwise - failure
213 
214   @ ref Developer
215 **/
216 int CeedOperatorGetActiveBasis(CeedOperator op, CeedBasis *active_basis) {
217   *active_basis = NULL;
218   for (int i = 0; i < op->qf->num_input_fields; i++)
219     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
220       *active_basis = op->input_fields[i]->basis;
221       break;
222     }
223 
224   if (!*active_basis) {
225     // LCOV_EXCL_START
226     int ierr;
227     Ceed ceed;
228     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
229     return CeedError(ceed, CEED_ERROR_MINOR,
230                      "No active CeedBasis found");
231     // LCOV_EXCL_STOP
232   }
233   return CEED_ERROR_SUCCESS;
234 }
235 
236 /**
237   @brief Find the active vector ElemRestriction for a CeedOperator
238 
239   @param[in] op            CeedOperator to find active basis for
240   @param[out] active_rstr  ElemRestriction for active input vector
241 
242   @return An error code: 0 - success, otherwise - failure
243 
244   @ref Utility
245 **/
246 int CeedOperatorGetActiveElemRestriction(CeedOperator op,
247     CeedElemRestriction *active_rstr) {
248   *active_rstr = NULL;
249   for (int i = 0; i < op->qf->num_input_fields; i++)
250     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
251       *active_rstr = op->input_fields[i]->elem_restr;
252       break;
253     }
254 
255   if (!*active_rstr) {
256     // LCOV_EXCL_START
257     int ierr;
258     Ceed ceed;
259     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
260     return CeedError(ceed, CEED_ERROR_INCOMPLETE,
261                      "No active CeedElemRestriction found");
262     // LCOV_EXCL_STOP
263   }
264   return CEED_ERROR_SUCCESS;
265 }
266 
267 /// @}
268 
269 /// ----------------------------------------------------------------------------
270 /// CeedOperator Backend API
271 /// ----------------------------------------------------------------------------
272 /// @addtogroup CeedOperatorBackend
273 /// @{
274 
275 /**
276   @brief Get the number of arguments associated with a CeedOperator
277 
278   @param op             CeedOperator
279   @param[out] num_args  Variable to store vector number of arguments
280 
281   @return An error code: 0 - success, otherwise - failure
282 
283   @ref Backend
284 **/
285 
286 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) {
287   if (op->is_composite)
288     // LCOV_EXCL_START
289     return CeedError(op->ceed, CEED_ERROR_MINOR,
290                      "Not defined for composite operators");
291   // LCOV_EXCL_STOP
292 
293   *num_args = op->num_fields;
294   return CEED_ERROR_SUCCESS;
295 }
296 
297 /**
298   @brief Get the setup status of a CeedOperator
299 
300   @param op                  CeedOperator
301   @param[out] is_setup_done  Variable to store setup status
302 
303   @return An error code: 0 - success, otherwise - failure
304 
305   @ref Backend
306 **/
307 
308 int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) {
309   *is_setup_done = op->is_backend_setup;
310   return CEED_ERROR_SUCCESS;
311 }
312 
313 /**
314   @brief Get the QFunction associated with a CeedOperator
315 
316   @param op       CeedOperator
317   @param[out] qf  Variable to store QFunction
318 
319   @return An error code: 0 - success, otherwise - failure
320 
321   @ref Backend
322 **/
323 
324 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
325   if (op->is_composite)
326     // LCOV_EXCL_START
327     return CeedError(op->ceed, CEED_ERROR_MINOR,
328                      "Not defined for composite operator");
329   // LCOV_EXCL_STOP
330 
331   *qf = op->qf;
332   return CEED_ERROR_SUCCESS;
333 }
334 
335 /**
336   @brief Get a boolean value indicating if the CeedOperator is composite
337 
338   @param op                 CeedOperator
339   @param[out] is_composite  Variable to store composite status
340 
341   @return An error code: 0 - success, otherwise - failure
342 
343   @ref Backend
344 **/
345 
346 int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) {
347   *is_composite = op->is_composite;
348   return CEED_ERROR_SUCCESS;
349 }
350 
351 /**
352   @brief Get the number of sub_operators associated with a CeedOperator
353 
354   @param op                     CeedOperator
355   @param[out] num_suboperators  Variable to store number of sub_operators
356 
357   @return An error code: 0 - success, otherwise - failure
358 
359   @ref Backend
360 **/
361 
362 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
363   if (!op->is_composite)
364     // LCOV_EXCL_START
365     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
366   // LCOV_EXCL_STOP
367 
368   *num_suboperators = op->num_suboperators;
369   return CEED_ERROR_SUCCESS;
370 }
371 
372 /**
373   @brief Get the list of sub_operators associated with a CeedOperator
374 
375   @param op                  CeedOperator
376   @param[out] sub_operators  Variable to store list of sub_operators
377 
378   @return An error code: 0 - success, otherwise - failure
379 
380   @ref Backend
381 **/
382 
383 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
384   if (!op->is_composite)
385     // LCOV_EXCL_START
386     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
387   // LCOV_EXCL_STOP
388 
389   *sub_operators = op->sub_operators;
390   return CEED_ERROR_SUCCESS;
391 }
392 
393 /**
394   @brief Get the backend data of a CeedOperator
395 
396   @param op         CeedOperator
397   @param[out] data  Variable to store data
398 
399   @return An error code: 0 - success, otherwise - failure
400 
401   @ref Backend
402 **/
403 
404 int CeedOperatorGetData(CeedOperator op, void *data) {
405   *(void **)data = op->data;
406   return CEED_ERROR_SUCCESS;
407 }
408 
409 /**
410   @brief Set the backend data of a CeedOperator
411 
412   @param[out] op  CeedOperator
413   @param data     Data to set
414 
415   @return An error code: 0 - success, otherwise - failure
416 
417   @ref Backend
418 **/
419 
420 int CeedOperatorSetData(CeedOperator op, void *data) {
421   op->data = data;
422   return CEED_ERROR_SUCCESS;
423 }
424 
425 /**
426   @brief Increment the reference counter for a CeedOperator
427 
428   @param op  CeedOperator to increment the reference counter
429 
430   @return An error code: 0 - success, otherwise - failure
431 
432   @ref Backend
433 **/
434 int CeedOperatorReference(CeedOperator op) {
435   op->ref_count++;
436   return CEED_ERROR_SUCCESS;
437 }
438 
439 /**
440   @brief Set the setup flag of a CeedOperator to True
441 
442   @param op  CeedOperator
443 
444   @return An error code: 0 - success, otherwise - failure
445 
446   @ref Backend
447 **/
448 
449 int CeedOperatorSetSetupDone(CeedOperator op) {
450   op->is_backend_setup = true;
451   return CEED_ERROR_SUCCESS;
452 }
453 
454 /// @}
455 
456 /// ----------------------------------------------------------------------------
457 /// CeedOperator Public API
458 /// ----------------------------------------------------------------------------
459 /// @addtogroup CeedOperatorUser
460 /// @{
461 
462 /**
463   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
464            CeedElemRestriction can be associated with CeedQFunction fields with
465            \ref CeedOperatorSetField.
466 
467   @param ceed     A Ceed object where the CeedOperator will be created
468   @param qf       QFunction defining the action of the operator at quadrature points
469   @param dqf      QFunction defining the action of the Jacobian of @a qf (or
470                     @ref CEED_QFUNCTION_NONE)
471   @param dqfT     QFunction defining the action of the transpose of the Jacobian
472                     of @a qf (or @ref CEED_QFUNCTION_NONE)
473   @param[out] op  Address of the variable where the newly created
474                     CeedOperator will be stored
475 
476   @return An error code: 0 - success, otherwise - failure
477 
478   @ref User
479  */
480 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
481                        CeedQFunction dqfT, CeedOperator *op) {
482   int ierr;
483 
484   if (!ceed->OperatorCreate) {
485     Ceed delegate;
486     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
487 
488     if (!delegate)
489       // LCOV_EXCL_START
490       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
491                        "Backend does not support OperatorCreate");
492     // LCOV_EXCL_STOP
493 
494     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
495     return CEED_ERROR_SUCCESS;
496   }
497 
498   if (!qf || qf == CEED_QFUNCTION_NONE)
499     // LCOV_EXCL_START
500     return CeedError(ceed, CEED_ERROR_MINOR,
501                      "Operator must have a valid QFunction.");
502   // LCOV_EXCL_STOP
503   ierr = CeedCalloc(1, op); CeedChk(ierr);
504   (*op)->ceed = ceed;
505   ierr = CeedReference(ceed); CeedChk(ierr);
506   (*op)->ref_count = 1;
507   (*op)->qf = qf;
508   ierr = CeedQFunctionReference(qf); CeedChk(ierr);
509   if (dqf && dqf != CEED_QFUNCTION_NONE) {
510     (*op)->dqf = dqf;
511     ierr = CeedQFunctionReference(dqf); CeedChk(ierr);
512   }
513   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
514     (*op)->dqfT = dqfT;
515     ierr = CeedQFunctionReference(dqfT); CeedChk(ierr);
516   }
517   ierr = CeedCalloc(16, &(*op)->input_fields); CeedChk(ierr);
518   ierr = CeedCalloc(16, &(*op)->output_fields); CeedChk(ierr);
519   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
520   return CEED_ERROR_SUCCESS;
521 }
522 
523 /**
524   @brief Create an operator that composes the action of several operators
525 
526   @param ceed     A Ceed object where the CeedOperator will be created
527   @param[out] op  Address of the variable where the newly created
528                     Composite CeedOperator will be stored
529 
530   @return An error code: 0 - success, otherwise - failure
531 
532   @ref User
533  */
534 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
535   int ierr;
536 
537   if (!ceed->CompositeOperatorCreate) {
538     Ceed delegate;
539     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
540 
541     if (delegate) {
542       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
543       return CEED_ERROR_SUCCESS;
544     }
545   }
546 
547   ierr = CeedCalloc(1, op); CeedChk(ierr);
548   (*op)->ceed = ceed;
549   ierr = CeedReference(ceed); CeedChk(ierr);
550   (*op)->is_composite = true;
551   ierr = CeedCalloc(16, &(*op)->sub_operators); CeedChk(ierr);
552 
553   if (ceed->CompositeOperatorCreate) {
554     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
555   }
556   return CEED_ERROR_SUCCESS;
557 }
558 
559 /**
560   @brief Copy the pointer to a CeedOperator. Both pointers should
561            be destroyed with `CeedOperatorDestroy()`;
562            Note: If `*op_copy` is non-NULL, then it is assumed that
563            `*op_copy` is a pointer to a CeedOperator. This
564            CeedOperator will be destroyed if `*op_copy` is the only
565            reference to this CeedOperator.
566 
567   @param op            CeedOperator to copy reference to
568   @param[out] op_copy  Variable to store copied reference
569 
570   @return An error code: 0 - success, otherwise - failure
571 
572   @ref User
573 **/
574 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
575   int ierr;
576 
577   ierr = CeedOperatorReference(op); CeedChk(ierr);
578   ierr = CeedOperatorDestroy(op_copy); CeedChk(ierr);
579   *op_copy = op;
580   return CEED_ERROR_SUCCESS;
581 }
582 
583 /**
584   @brief Provide a field to a CeedOperator for use by its CeedQFunction
585 
586   This function is used to specify both active and passive fields to a
587   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
588   fields can inputs or outputs (updated in-place when operator is applied).
589 
590   Active fields must be specified using this function, but their data (in a
591   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
592   input and at most one active output.
593 
594   @param op          CeedOperator on which to provide the field
595   @param field_name  Name of the field (to be matched with the name used by
596                        CeedQFunction)
597   @param r           CeedElemRestriction
598   @param b           CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
599                        if collocated with quadrature points
600   @param v           CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
601                        if field is active or @ref CEED_VECTOR_NONE if using
602                        @ref CEED_EVAL_WEIGHT in the QFunction
603 
604   @return An error code: 0 - success, otherwise - failure
605 
606   @ref User
607 **/
608 int CeedOperatorSetField(CeedOperator op, const char *field_name,
609                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
610   int ierr;
611   if (op->is_composite)
612     // LCOV_EXCL_START
613     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
614                      "Cannot add field to composite operator.");
615   // LCOV_EXCL_STOP
616   if (op->is_immutable)
617     // LCOV_EXCL_START
618     return CeedError(op->ceed, CEED_ERROR_MAJOR,
619                      "Operator cannot be changed after set as immutable");
620   // LCOV_EXCL_STOP
621   if (!r)
622     // LCOV_EXCL_START
623     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
624                      "ElemRestriction r for field \"%s\" must be non-NULL.",
625                      field_name);
626   // LCOV_EXCL_STOP
627   if (!b)
628     // LCOV_EXCL_START
629     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
630                      "Basis b for field \"%s\" must be non-NULL.",
631                      field_name);
632   // LCOV_EXCL_STOP
633   if (!v)
634     // LCOV_EXCL_START
635     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
636                      "Vector v for field \"%s\" must be non-NULL.",
637                      field_name);
638   // LCOV_EXCL_STOP
639 
640   CeedInt num_elem;
641   ierr = CeedElemRestrictionGetNumElements(r, &num_elem); CeedChk(ierr);
642   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction &&
643       op->num_elem != num_elem)
644     // LCOV_EXCL_START
645     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
646                      "ElemRestriction with %d elements incompatible with prior "
647                      "%d elements", num_elem, op->num_elem);
648   // LCOV_EXCL_STOP
649 
650   CeedInt num_qpts = 0;
651   if (b != CEED_BASIS_COLLOCATED) {
652     ierr = CeedBasisGetNumQuadraturePoints(b, &num_qpts); CeedChk(ierr);
653     if (op->num_qpts && op->num_qpts != num_qpts)
654       // LCOV_EXCL_START
655       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
656                        "Basis with %d quadrature points "
657                        "incompatible with prior %d points", num_qpts,
658                        op->num_qpts);
659     // LCOV_EXCL_STOP
660   }
661   CeedQFunctionField qf_field;
662   CeedOperatorField *op_field;
663   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
664     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
665       qf_field = op->qf->input_fields[i];
666       op_field = &op->input_fields[i];
667       goto found;
668     }
669   }
670   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
671     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
672       qf_field = op->qf->output_fields[i];
673       op_field = &op->output_fields[i];
674       goto found;
675     }
676   }
677   // LCOV_EXCL_START
678   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
679                    "QFunction has no knowledge of field '%s'",
680                    field_name);
681   // LCOV_EXCL_STOP
682 found:
683   ierr = CeedOperatorCheckField(op->ceed, qf_field, r, b); CeedChk(ierr);
684   ierr = CeedCalloc(1, op_field); CeedChk(ierr);
685 
686   (*op_field)->vec = v;
687   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
688     ierr = CeedVectorReference(v); CeedChk(ierr);
689   }
690 
691   (*op_field)->elem_restr = r;
692   ierr = CeedElemRestrictionReference(r); CeedChk(ierr);
693   if (r != CEED_ELEMRESTRICTION_NONE) {
694     op->num_elem = num_elem;
695     op->has_restriction = true; // Restriction set, but num_elem may be 0
696   }
697 
698   (*op_field)->basis = b;
699   if (b != CEED_BASIS_COLLOCATED) {
700     if (!op->num_qpts) {
701       ierr = CeedOperatorSetNumQuadraturePoints(op, num_qpts); CeedChk(ierr);
702     }
703     ierr = CeedBasisReference(b); CeedChk(ierr);
704   }
705 
706   op->num_fields += 1;
707   size_t len = strlen(field_name);
708   char *tmp;
709   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
710   memcpy(tmp, field_name, len+1);
711   (*op_field)->field_name = tmp;
712   return CEED_ERROR_SUCCESS;
713 }
714 
715 /**
716   @brief Get the CeedOperatorFields of a CeedOperator
717 
718   Note: Calling this function asserts that setup is complete
719           and sets the CeedOperator as immutable.
720 
721   @param op                      CeedOperator
722   @param[out] num_input_fields   Variable to store number of input fields
723   @param[out] input_fields       Variable to store input_fields
724   @param[out] num_output_fields  Variable to store number of output fields
725   @param[out] output_fields      Variable to store output_fields
726 
727   @return An error code: 0 - success, otherwise - failure
728 
729   @ref Advanced
730 **/
731 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields,
732                           CeedOperatorField **input_fields,
733                           CeedInt *num_output_fields,
734                           CeedOperatorField **output_fields) {
735   int ierr;
736 
737   if (op->is_composite)
738     // LCOV_EXCL_START
739     return CeedError(op->ceed, CEED_ERROR_MINOR,
740                      "Not defined for composite operator");
741   // LCOV_EXCL_STOP
742   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
743 
744   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
745   if (input_fields) *input_fields = op->input_fields;
746   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
747   if (output_fields) *output_fields = op->output_fields;
748   return CEED_ERROR_SUCCESS;
749 }
750 
751 /**
752   @brief Get the name of a CeedOperatorField
753 
754   @param op_field         CeedOperatorField
755   @param[out] field_name  Variable to store the field name
756 
757   @return An error code: 0 - success, otherwise - failure
758 
759   @ref Advanced
760 **/
761 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
762   *field_name = (char *)op_field->field_name;
763   return CEED_ERROR_SUCCESS;
764 }
765 
766 /**
767   @brief Get the CeedElemRestriction of a CeedOperatorField
768 
769   @param op_field   CeedOperatorField
770   @param[out] rstr  Variable to store CeedElemRestriction
771 
772   @return An error code: 0 - success, otherwise - failure
773 
774   @ref Advanced
775 **/
776 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field,
777                                         CeedElemRestriction *rstr) {
778   *rstr = op_field->elem_restr;
779   return CEED_ERROR_SUCCESS;
780 }
781 
782 /**
783   @brief Get the CeedBasis of a CeedOperatorField
784 
785   @param op_field    CeedOperatorField
786   @param[out] basis  Variable to store CeedBasis
787 
788   @return An error code: 0 - success, otherwise - failure
789 
790   @ref Advanced
791 **/
792 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
793   *basis = op_field->basis;
794   return CEED_ERROR_SUCCESS;
795 }
796 
797 /**
798   @brief Get the CeedVector of a CeedOperatorField
799 
800   @param op_field  CeedOperatorField
801   @param[out] vec  Variable to store CeedVector
802 
803   @return An error code: 0 - success, otherwise - failure
804 
805   @ref Advanced
806 **/
807 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
808   *vec = op_field->vec;
809   return CEED_ERROR_SUCCESS;
810 }
811 
812 /**
813   @brief Add a sub-operator to a composite CeedOperator
814 
815   @param[out] composite_op  Composite CeedOperator
816   @param      sub_op        Sub-operator CeedOperator
817 
818   @return An error code: 0 - success, otherwise - failure
819 
820   @ref User
821  */
822 int CeedCompositeOperatorAddSub(CeedOperator composite_op,
823                                 CeedOperator sub_op) {
824   int ierr;
825 
826   if (!composite_op->is_composite)
827     // LCOV_EXCL_START
828     return CeedError(composite_op->ceed, CEED_ERROR_MINOR,
829                      "CeedOperator is not a composite operator");
830   // LCOV_EXCL_STOP
831 
832   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX)
833     // LCOV_EXCL_START
834     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED,
835                      "Cannot add additional sub_operators");
836   // LCOV_EXCL_STOP
837   if (composite_op->is_immutable)
838     // LCOV_EXCL_START
839     return CeedError(composite_op->ceed, CEED_ERROR_MAJOR,
840                      "Operator cannot be changed after set as immutable");
841   // LCOV_EXCL_STOP
842 
843   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
844   ierr = CeedOperatorReference(sub_op); CeedChk(ierr);
845   composite_op->num_suboperators++;
846   return CEED_ERROR_SUCCESS;
847 }
848 
849 /**
850   @brief Check if a CeedOperator is ready to be used.
851 
852   @param[in] op  CeedOperator to check
853 
854   @return An error code: 0 - success, otherwise - failure
855 
856   @ref User
857 **/
858 int CeedOperatorCheckReady(CeedOperator op) {
859   int ierr;
860   Ceed ceed;
861   ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
862 
863   if (op->is_interface_setup)
864     return CEED_ERROR_SUCCESS;
865 
866   CeedQFunction qf = op->qf;
867   if (op->is_composite) {
868     if (!op->num_suboperators)
869       // LCOV_EXCL_START
870       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No sub_operators set");
871     // LCOV_EXCL_STOP
872     for (CeedInt i = 0; i < op->num_suboperators; i++) {
873       ierr = CeedOperatorCheckReady(op->sub_operators[i]); CeedChk(ierr);
874     }
875   } else {
876     if (op->num_fields == 0)
877       // LCOV_EXCL_START
878       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
879     // LCOV_EXCL_STOP
880     if (op->num_fields < qf->num_input_fields + qf->num_output_fields)
881       // LCOV_EXCL_START
882       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
883     // LCOV_EXCL_STOP
884     if (!op->has_restriction)
885       // LCOV_EXCL_START
886       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
887                        "At least one restriction required");
888     // LCOV_EXCL_STOP
889     if (op->num_qpts == 0)
890       // LCOV_EXCL_START
891       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
892                        "At least one non-collocated basis is required "
893                        "or the number of quadrature points must be set");
894     // LCOV_EXCL_STOP
895   }
896 
897   // Flag as immutable and ready
898   op->is_interface_setup = true;
899   if (op->qf && op->qf != CEED_QFUNCTION_NONE)
900     // LCOV_EXCL_START
901     op->qf->is_immutable = true;
902   // LCOV_EXCL_STOP
903   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE)
904     // LCOV_EXCL_START
905     op->dqf->is_immutable = true;
906   // LCOV_EXCL_STOP
907   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE)
908     // LCOV_EXCL_START
909     op->dqfT->is_immutable = true;
910   // LCOV_EXCL_STOP
911   return CEED_ERROR_SUCCESS;
912 }
913 
914 /**
915   @brief Set the number of quadrature points associated with a CeedOperator.
916            This should be used when creating a CeedOperator where every
917            field has a collocated basis. This function cannot be used for
918            composite CeedOperators.
919 
920   @param op        CeedOperator
921   @param num_qpts  Number of quadrature points to set
922 
923   @return An error code: 0 - success, otherwise - failure
924 
925   @ref Advanced
926 **/
927 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
928   if (op->is_composite)
929     // LCOV_EXCL_START
930     return CeedError(op->ceed, CEED_ERROR_MINOR,
931                      "Not defined for composite operator");
932   // LCOV_EXCL_STOP
933   if (op->num_qpts)
934     // LCOV_EXCL_START
935     return CeedError(op->ceed, CEED_ERROR_MINOR,
936                      "Number of quadrature points already defined");
937   // LCOV_EXCL_STOP
938   if (op->is_immutable)
939     // LCOV_EXCL_START
940     return CeedError(op->ceed, CEED_ERROR_MAJOR,
941                      "Operator cannot be changed after set as immutable");
942   // LCOV_EXCL_STOP
943 
944   op->num_qpts = num_qpts;
945   return CEED_ERROR_SUCCESS;
946 }
947 
948 /**
949   @brief View a CeedOperator
950 
951   @param[in] op      CeedOperator to view
952   @param[in] stream  Stream to write; typically stdout/stderr or a file
953 
954   @return Error code: 0 - success, otherwise - failure
955 
956   @ref User
957 **/
958 int CeedOperatorView(CeedOperator op, FILE *stream) {
959   int ierr;
960 
961   if (op->is_composite) {
962     fprintf(stream, "Composite CeedOperator\n");
963 
964     for (CeedInt i=0; i<op->num_suboperators; i++) {
965       fprintf(stream, "  SubOperator [%d]:\n", i);
966       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
967       CeedChk(ierr);
968     }
969   } else {
970     fprintf(stream, "CeedOperator\n");
971     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
972   }
973   return CEED_ERROR_SUCCESS;
974 }
975 
976 /**
977   @brief Get the Ceed associated with a CeedOperator
978 
979   @param op         CeedOperator
980   @param[out] ceed  Variable to store Ceed
981 
982   @return An error code: 0 - success, otherwise - failure
983 
984   @ref Advanced
985 **/
986 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
987   *ceed = op->ceed;
988   return CEED_ERROR_SUCCESS;
989 }
990 
991 /**
992   @brief Get the number of elements associated with a CeedOperator
993 
994   @param op             CeedOperator
995   @param[out] num_elem  Variable to store number of elements
996 
997   @return An error code: 0 - success, otherwise - failure
998 
999   @ref Advanced
1000 **/
1001 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1002   if (op->is_composite)
1003     // LCOV_EXCL_START
1004     return CeedError(op->ceed, CEED_ERROR_MINOR,
1005                      "Not defined for composite operator");
1006   // LCOV_EXCL_STOP
1007 
1008   *num_elem = op->num_elem;
1009   return CEED_ERROR_SUCCESS;
1010 }
1011 
1012 /**
1013   @brief Get the number of quadrature points associated with a CeedOperator
1014 
1015   @param op             CeedOperator
1016   @param[out] num_qpts  Variable to store vector number of quadrature points
1017 
1018   @return An error code: 0 - success, otherwise - failure
1019 
1020   @ref Advanced
1021 **/
1022 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1023   if (op->is_composite)
1024     // LCOV_EXCL_START
1025     return CeedError(op->ceed, CEED_ERROR_MINOR,
1026                      "Not defined for composite operator");
1027   // LCOV_EXCL_STOP
1028 
1029   *num_qpts = op->num_qpts;
1030   return CEED_ERROR_SUCCESS;
1031 }
1032 
1033 /**
1034   @brief Apply CeedOperator to a vector
1035 
1036   This computes the action of the operator on the specified (active) input,
1037   yielding its (active) output.  All inputs and outputs must be specified using
1038   CeedOperatorSetField().
1039 
1040   Note: Calling this function asserts that setup is complete
1041           and sets the CeedOperator as immutable.
1042 
1043   @param op        CeedOperator to apply
1044   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1045                      there are no active inputs
1046   @param[out] out  CeedVector to store result of applying operator (must be
1047                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1048                      active outputs
1049   @param request   Address of CeedRequest for non-blocking completion, else
1050                      @ref CEED_REQUEST_IMMEDIATE
1051 
1052   @return An error code: 0 - success, otherwise - failure
1053 
1054   @ref User
1055 **/
1056 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1057                       CeedRequest *request) {
1058   int ierr;
1059   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1060 
1061   if (op->num_elem)  {
1062     // Standard Operator
1063     if (op->Apply) {
1064       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1065     } else {
1066       // Zero all output vectors
1067       CeedQFunction qf = op->qf;
1068       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1069         CeedVector vec = op->output_fields[i]->vec;
1070         if (vec == CEED_VECTOR_ACTIVE)
1071           vec = out;
1072         if (vec != CEED_VECTOR_NONE) {
1073           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1074         }
1075       }
1076       // Apply
1077       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1078     }
1079   } else if (op->is_composite) {
1080     // Composite Operator
1081     if (op->ApplyComposite) {
1082       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1083     } else {
1084       CeedInt num_suboperators;
1085       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1086       CeedOperator *sub_operators;
1087       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1088 
1089       // Zero all output vectors
1090       if (out != CEED_VECTOR_NONE) {
1091         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1092       }
1093       for (CeedInt i=0; i<num_suboperators; i++) {
1094         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1095           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1096           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1097             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1098           }
1099         }
1100       }
1101       // Apply
1102       for (CeedInt i=0; i<op->num_suboperators; i++) {
1103         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1104         CeedChk(ierr);
1105       }
1106     }
1107   }
1108   return CEED_ERROR_SUCCESS;
1109 }
1110 
1111 /**
1112   @brief Apply CeedOperator to a vector and add result to output vector
1113 
1114   This computes the action of the operator on the specified (active) input,
1115   yielding its (active) output.  All inputs and outputs must be specified using
1116   CeedOperatorSetField().
1117 
1118   @param op        CeedOperator to apply
1119   @param[in] in    CeedVector containing input state or NULL if there are no
1120                      active inputs
1121   @param[out] out  CeedVector to sum in result of applying operator (must be
1122                      distinct from @a in) or NULL if there are no active outputs
1123   @param request   Address of CeedRequest for non-blocking completion, else
1124                      @ref CEED_REQUEST_IMMEDIATE
1125 
1126   @return An error code: 0 - success, otherwise - failure
1127 
1128   @ref User
1129 **/
1130 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1131                          CeedRequest *request) {
1132   int ierr;
1133   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1134 
1135   if (op->num_elem)  {
1136     // Standard Operator
1137     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1138   } else if (op->is_composite) {
1139     // Composite Operator
1140     if (op->ApplyAddComposite) {
1141       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1142     } else {
1143       CeedInt num_suboperators;
1144       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1145       CeedOperator *sub_operators;
1146       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1147 
1148       for (CeedInt i=0; i<num_suboperators; i++) {
1149         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1150         CeedChk(ierr);
1151       }
1152     }
1153   }
1154   return CEED_ERROR_SUCCESS;
1155 }
1156 
1157 /**
1158   @brief Destroy a CeedOperator
1159 
1160   @param op  CeedOperator to destroy
1161 
1162   @return An error code: 0 - success, otherwise - failure
1163 
1164   @ref User
1165 **/
1166 int CeedOperatorDestroy(CeedOperator *op) {
1167   int ierr;
1168 
1169   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1170   if ((*op)->Destroy) {
1171     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1172   }
1173   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1174   // Free fields
1175   for (int i=0; i<(*op)->num_fields; i++)
1176     if ((*op)->input_fields[i]) {
1177       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1178         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1179         CeedChk(ierr);
1180       }
1181       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1182         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1183       }
1184       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1185           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1186         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1187       }
1188       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1189       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1190     }
1191   for (int i=0; i<(*op)->num_fields; i++)
1192     if ((*op)->output_fields[i]) {
1193       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1194       CeedChk(ierr);
1195       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1196         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1197       }
1198       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1199           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1200         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1201       }
1202       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1203       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1204     }
1205   // Destroy sub_operators
1206   for (int i=0; i<(*op)->num_suboperators; i++)
1207     if ((*op)->sub_operators[i]) {
1208       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1209     }
1210   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1211   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1212   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1213 
1214   // Destroy fallback
1215   if ((*op)->op_fallback) {
1216     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1217     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1218     ierr = CeedVectorDestroy(&(*op)->op_fallback->qf_assembled); CeedChk(ierr);
1219     ierr = CeedElemRestrictionDestroy(&(*op)->op_fallback->qf_assembled_rstr);
1220     CeedChk(ierr);
1221     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1222     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1223   }
1224 
1225   // Destroy QF assembly cache
1226   ierr = CeedVectorDestroy(&(*op)->qf_assembled); CeedChk(ierr);
1227   ierr = CeedElemRestrictionDestroy(&(*op)->qf_assembled_rstr); CeedChk(ierr);
1228 
1229   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1230   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1231   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1232   ierr = CeedFree(op); CeedChk(ierr);
1233   return CEED_ERROR_SUCCESS;
1234 }
1235 
1236 /// @}
1237