xref: /libCEED/interface/ceed-operator.c (revision 58c07c4fa7bdba34c2b29fbdcd58893d48c3fd9e)
1 // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3 //
4 // SPDX-License-Identifier: BSD-2-Clause
5 //
6 // This file is part of CEED:  http://github.com/ceed
7 
8 #include <ceed-impl.h>
9 #include <ceed.h>
10 #include <ceed/backend.h>
11 #include <stdbool.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 /// @file
16 /// Implementation of CeedOperator interfaces
17 
18 /// ----------------------------------------------------------------------------
19 /// CeedOperator Library Internal Functions
20 /// ----------------------------------------------------------------------------
21 /// @addtogroup CeedOperatorDeveloper
22 /// @{
23 
24 /**
25   @brief Check if a CeedOperator Field matches the QFunction Field
26 
27   @param[in] ceed     Ceed object for error handling
28   @param[in] qf_field QFunction Field matching Operator Field
29   @param[in] r        Operator Field ElemRestriction
30   @param[in] b        Operator Field Basis
31 
32   @return An error code: 0 - success, otherwise - failure
33 
34   @ref Developer
35 **/
36 static int CeedOperatorCheckField(Ceed ceed, CeedQFunctionField qf_field, CeedElemRestriction r, CeedBasis b) {
37   CeedInt      dim = 1, num_comp = 1, q_comp = 1, rstr_num_comp = 1, size = qf_field->size;
38   CeedEvalMode eval_mode = qf_field->eval_mode;
39 
40   // Restriction
41   CeedCheck((r == CEED_ELEMRESTRICTION_NONE) == (eval_mode == CEED_EVAL_WEIGHT), ceed, CEED_ERROR_INCOMPATIBLE,
42             "CEED_ELEMRESTRICTION_NONE and CEED_EVAL_WEIGHT must be used together.");
43   if (r != CEED_ELEMRESTRICTION_NONE) {
44     CeedCall(CeedElemRestrictionGetNumComponents(r, &rstr_num_comp));
45   }
46   // Basis
47   CeedCheck((b == CEED_BASIS_NONE) == (eval_mode == CEED_EVAL_NONE), ceed, CEED_ERROR_INCOMPATIBLE,
48             "CEED_BASIS_NONE and CEED_EVAL_NONE must be used together.");
49   if (b != CEED_BASIS_NONE) {
50     CeedCall(CeedBasisGetDimension(b, &dim));
51     CeedCall(CeedBasisGetNumComponents(b, &num_comp));
52     CeedCall(CeedBasisGetNumQuadratureComponents(b, eval_mode, &q_comp));
53     CeedCheck(r == CEED_ELEMRESTRICTION_NONE || rstr_num_comp == num_comp, ceed, CEED_ERROR_DIMENSION,
54               "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction has %" CeedInt_FMT " components, but Basis has %" CeedInt_FMT
55               " components",
56               qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], rstr_num_comp, num_comp);
57   }
58   // Field size
59   switch (eval_mode) {
60     case CEED_EVAL_NONE:
61       CeedCheck(size == rstr_num_comp, ceed, CEED_ERROR_DIMENSION,
62                 "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction has %" CeedInt_FMT " components", qf_field->field_name,
63                 qf_field->size, CeedEvalModes[qf_field->eval_mode], rstr_num_comp);
64       break;
65     case CEED_EVAL_INTERP:
66     case CEED_EVAL_GRAD:
67     case CEED_EVAL_DIV:
68     case CEED_EVAL_CURL:
69       CeedCheck(size == num_comp * q_comp, ceed, CEED_ERROR_DIMENSION,
70                 "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction/Basis has %" CeedInt_FMT " components", qf_field->field_name,
71                 qf_field->size, CeedEvalModes[qf_field->eval_mode], num_comp * q_comp);
72       break;
73     case CEED_EVAL_WEIGHT:
74       // No additional checks required
75       break;
76   }
77   return CEED_ERROR_SUCCESS;
78 }
79 
80 /**
81   @brief View a field of a CeedOperator
82 
83   @param[in] field        Operator field to view
84   @param[in] qf_field     QFunction field (carries field name)
85   @param[in] field_number Number of field being viewed
86   @param[in] sub          true indicates sub-operator, which increases indentation; false for top-level operator
87   @param[in] input        true for an input field; false for output field
88   @param[in] stream       Stream to view to, e.g., stdout
89 
90   @return An error code: 0 - success, otherwise - failure
91 
92   @ref Utility
93 **/
94 static int CeedOperatorFieldView(CeedOperatorField field, CeedQFunctionField qf_field, CeedInt field_number, bool sub, bool input, FILE *stream) {
95   const char *pre    = sub ? "  " : "";
96   const char *in_out = input ? "Input" : "Output";
97 
98   fprintf(stream,
99           "%s    %s field %" CeedInt_FMT
100           ":\n"
101           "%s      Name: \"%s\"\n",
102           pre, in_out, field_number, pre, qf_field->field_name);
103   fprintf(stream, "%s      Size: %" CeedInt_FMT "\n", pre, qf_field->size);
104   fprintf(stream, "%s      EvalMode: %s\n", pre, CeedEvalModes[qf_field->eval_mode]);
105   if (field->basis == CEED_BASIS_NONE) fprintf(stream, "%s      No basis\n", pre);
106   if (field->vec == CEED_VECTOR_ACTIVE) fprintf(stream, "%s      Active vector\n", pre);
107   else if (field->vec == CEED_VECTOR_NONE) fprintf(stream, "%s      No vector\n", pre);
108   return CEED_ERROR_SUCCESS;
109 }
110 
111 /**
112   @brief View a single CeedOperator
113 
114   @param[in] op     CeedOperator to view
115   @param[in] sub    Boolean flag for sub-operator
116   @param[in] stream Stream to write; typically stdout/stderr or a file
117 
118   @return Error code: 0 - success, otherwise - failure
119 
120   @ref Utility
121 **/
122 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
123   const char *pre = sub ? "  " : "";
124   CeedInt     num_elem, num_qpts, total_fields = 0;
125 
126   CeedCall(CeedOperatorGetNumElements(op, &num_elem));
127   CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts));
128   CeedCall(CeedOperatorGetNumArgs(op, &total_fields));
129 
130   fprintf(stream, "%s  %" CeedInt_FMT " elements with %" CeedInt_FMT " quadrature points each\n", pre, num_elem, num_qpts);
131   fprintf(stream, "%s  %" CeedInt_FMT " field%s\n", pre, total_fields, total_fields > 1 ? "s" : "");
132   fprintf(stream, "%s  %" CeedInt_FMT " input field%s:\n", pre, op->qf->num_input_fields, op->qf->num_input_fields > 1 ? "s" : "");
133   for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
134     CeedCall(CeedOperatorFieldView(op->input_fields[i], op->qf->input_fields[i], i, sub, 1, stream));
135   }
136   fprintf(stream, "%s  %" CeedInt_FMT " output field%s:\n", pre, op->qf->num_output_fields, op->qf->num_output_fields > 1 ? "s" : "");
137   for (CeedInt i = 0; i < op->qf->num_output_fields; i++) {
138     CeedCall(CeedOperatorFieldView(op->output_fields[i], op->qf->output_fields[i], i, sub, 0, stream));
139   }
140   return CEED_ERROR_SUCCESS;
141 }
142 
143 /**
144   @brief Find the active vector basis for a non-composite CeedOperator
145 
146   @param[in] op            CeedOperator to find active basis for
147   @param[out] active_basis Basis for active input vector or NULL for composite operator
148 
149   @return An error code: 0 - success, otherwise - failure
150 
151   @ref Developer
152 **/
153 int CeedOperatorGetActiveBasis(CeedOperator op, CeedBasis *active_basis) {
154   Ceed ceed;
155 
156   CeedCall(CeedOperatorGetCeed(op, &ceed));
157 
158   *active_basis = NULL;
159   if (op->is_composite) return CEED_ERROR_SUCCESS;
160   for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
161     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
162       CeedCheck(!*active_basis || *active_basis == op->input_fields[i]->basis, ceed, CEED_ERROR_MINOR, "Multiple active CeedBases found");
163       *active_basis = op->input_fields[i]->basis;
164     }
165   }
166 
167   CeedCheck(*active_basis, ceed, CEED_ERROR_INCOMPLETE, "No active CeedBasis found");
168   return CEED_ERROR_SUCCESS;
169 }
170 
171 /**
172   @brief Find the active vector ElemRestriction for a non-composite CeedOperator
173 
174   @param[in] op           CeedOperator to find active basis for
175   @param[out] active_rstr ElemRestriction for active input vector or NULL for composite operator
176 
177   @return An error code: 0 - success, otherwise - failure
178 
179   @ref Utility
180 **/
181 int CeedOperatorGetActiveElemRestriction(CeedOperator op, CeedElemRestriction *active_rstr) {
182   Ceed ceed;
183 
184   CeedCall(CeedOperatorGetCeed(op, &ceed));
185 
186   *active_rstr = NULL;
187   if (op->is_composite) return CEED_ERROR_SUCCESS;
188   for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
189     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
190       CeedCheck(!*active_rstr || *active_rstr == op->input_fields[i]->elem_rstr, ceed, CEED_ERROR_MINOR,
191                 "Multiple active CeedElemRestrictions found");
192       *active_rstr = op->input_fields[i]->elem_rstr;
193     }
194   }
195 
196   CeedCheck(*active_rstr, ceed, CEED_ERROR_INCOMPLETE, "No active CeedElemRestriction found");
197   return CEED_ERROR_SUCCESS;
198 }
199 
200 /**
201   @brief Set QFunctionContext field values of the specified type.
202 
203   For composite operators, the value is set in all sub-operator QFunctionContexts that have a matching `field_name`.
204   A non-zero error code is returned for single operators that do not have a matching field of the same type or composite operators that do not have
205 any field of a matching type.
206 
207   @param[in,out] op          CeedOperator
208   @param[in]     field_label Label of field to set
209   @param[in]     field_type  Type of field to set
210   @param[in]     values      Values to set
211 
212   @return An error code: 0 - success, otherwise - failure
213 
214   @ref User
215 **/
216 static int CeedOperatorContextSetGeneric(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, void *values) {
217   bool is_composite = false;
218 
219   CeedCheck(field_label, op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label");
220 
221   // Check if field_label and op correspond
222   if (field_label->from_op) {
223     CeedInt index = -1;
224 
225     for (CeedInt i = 0; i < op->num_context_labels; i++) {
226       if (op->context_labels[i] == field_label) index = i;
227     }
228     CeedCheck(index != -1, op->ceed, CEED_ERROR_UNSUPPORTED, "ContextFieldLabel does not correspond to the operator");
229   }
230 
231   CeedCall(CeedOperatorIsComposite(op, &is_composite));
232   if (is_composite) {
233     CeedInt       num_sub;
234     CeedOperator *sub_operators;
235 
236     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
237     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
238     CeedCheck(num_sub == field_label->num_sub_labels, op->ceed, CEED_ERROR_UNSUPPORTED,
239               "Composite operator modified after ContextFieldLabel created");
240 
241     for (CeedInt i = 0; i < num_sub; i++) {
242       // Try every sub-operator, ok if some sub-operators do not have field
243       if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) {
244         CeedCall(CeedQFunctionContextSetGeneric(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, values));
245       }
246     }
247   } else {
248     CeedCheck(op->qf->ctx, op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data");
249     CeedCall(CeedQFunctionContextSetGeneric(op->qf->ctx, field_label, field_type, values));
250   }
251   return CEED_ERROR_SUCCESS;
252 }
253 
254 /**
255   @brief Get QFunctionContext field values of the specified type, read-only.
256 
257   For composite operators, the values retrieved are for the first sub-operator QFunctionContext that have a matching `field_name`.
258   A non-zero error code is returned for single operators that do not have a matching field of the same type or composite operators that do not have
259 any field of a matching type.
260 
261   @param[in,out] op          CeedOperator
262   @param[in]     field_label Label of field to set
263   @param[in]     field_type  Type of field to set
264   @param[out]    num_values  Number of values of type `field_type` in array `values`
265   @param[out]    values      Values in the label
266 
267   @return An error code: 0 - success, otherwise - failure
268 
269   @ref User
270 **/
271 static int CeedOperatorContextGetGenericRead(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, size_t *num_values,
272                                              void *values) {
273   bool is_composite = false;
274 
275   CeedCheck(field_label, op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label");
276 
277   *(void **)values = NULL;
278   *num_values      = 0;
279 
280   // Check if field_label and op correspond
281   if (field_label->from_op) {
282     CeedInt index = -1;
283 
284     for (CeedInt i = 0; i < op->num_context_labels; i++) {
285       if (op->context_labels[i] == field_label) index = i;
286     }
287     CeedCheck(index != -1, op->ceed, CEED_ERROR_UNSUPPORTED, "ContextFieldLabel does not correspond to the operator");
288   }
289 
290   CeedCall(CeedOperatorIsComposite(op, &is_composite));
291   if (is_composite) {
292     CeedInt       num_sub;
293     CeedOperator *sub_operators;
294 
295     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
296     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
297     CeedCheck(num_sub == field_label->num_sub_labels, op->ceed, CEED_ERROR_UNSUPPORTED,
298               "Composite operator modified after ContextFieldLabel created");
299 
300     for (CeedInt i = 0; i < num_sub; i++) {
301       // Try every sub-operator, ok if some sub-operators do not have field
302       if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) {
303         CeedCall(CeedQFunctionContextGetGenericRead(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, num_values, values));
304         return CEED_ERROR_SUCCESS;
305       }
306     }
307   } else {
308     CeedCheck(op->qf->ctx, op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data");
309     CeedCall(CeedQFunctionContextGetGenericRead(op->qf->ctx, field_label, field_type, num_values, values));
310   }
311   return CEED_ERROR_SUCCESS;
312 }
313 
314 /**
315   @brief Restore QFunctionContext field values of the specified type, read-only.
316 
317   For composite operators, the values restored are for the first sub-operator QFunctionContext that have a matching `field_name`.
318   A non-zero error code is returned for single operators that do not have a matching field of the same type or composite operators that do not have
319 any field of a matching type.
320 
321   @param[in,out] op          CeedOperator
322   @param[in]     field_label Label of field to set
323   @param[in]     field_type  Type of field to set
324   @param[in]     values      Values array to restore
325 
326   @return An error code: 0 - success, otherwise - failure
327 
328   @ref User
329 **/
330 static int CeedOperatorContextRestoreGenericRead(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, void *values) {
331   bool is_composite = false;
332 
333   CeedCheck(field_label, op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label");
334 
335   // Check if field_label and op correspond
336   if (field_label->from_op) {
337     CeedInt index = -1;
338 
339     for (CeedInt i = 0; i < op->num_context_labels; i++) {
340       if (op->context_labels[i] == field_label) index = i;
341     }
342     CeedCheck(index != -1, op->ceed, CEED_ERROR_UNSUPPORTED, "ContextFieldLabel does not correspond to the operator");
343   }
344 
345   CeedCall(CeedOperatorIsComposite(op, &is_composite));
346   if (is_composite) {
347     CeedInt       num_sub;
348     CeedOperator *sub_operators;
349 
350     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
351     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
352     CeedCheck(num_sub == field_label->num_sub_labels, op->ceed, CEED_ERROR_UNSUPPORTED,
353               "Composite operator modified after ContextFieldLabel created");
354 
355     for (CeedInt i = 0; i < num_sub; i++) {
356       // Try every sub-operator, ok if some sub-operators do not have field
357       if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) {
358         CeedCall(CeedQFunctionContextRestoreGenericRead(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, values));
359         return CEED_ERROR_SUCCESS;
360       }
361     }
362   } else {
363     CeedCheck(op->qf->ctx, op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data");
364     CeedCall(CeedQFunctionContextRestoreGenericRead(op->qf->ctx, field_label, field_type, values));
365   }
366   return CEED_ERROR_SUCCESS;
367 }
368 
369 /// @}
370 
371 /// ----------------------------------------------------------------------------
372 /// CeedOperator Backend API
373 /// ----------------------------------------------------------------------------
374 /// @addtogroup CeedOperatorBackend
375 /// @{
376 
377 /**
378   @brief Get the number of arguments associated with a CeedOperator
379 
380   @param[in]  op        CeedOperator
381   @param[out] num_args  Variable to store vector number of arguments
382 
383   @return An error code: 0 - success, otherwise - failure
384 
385   @ref Backend
386 **/
387 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) {
388   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operators");
389   *num_args = op->num_fields;
390   return CEED_ERROR_SUCCESS;
391 }
392 
393 /**
394   @brief Get the setup status of a CeedOperator
395 
396   @param[in]  op            CeedOperator
397   @param[out] is_setup_done Variable to store setup status
398 
399   @return An error code: 0 - success, otherwise - failure
400 
401   @ref Backend
402 **/
403 int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) {
404   *is_setup_done = op->is_backend_setup;
405   return CEED_ERROR_SUCCESS;
406 }
407 
408 /**
409   @brief Get the QFunction associated with a CeedOperator
410 
411   @param[in]  op CeedOperator
412   @param[out] qf Variable to store QFunction
413 
414   @return An error code: 0 - success, otherwise - failure
415 
416   @ref Backend
417 **/
418 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
419   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
420   *qf = op->qf;
421   return CEED_ERROR_SUCCESS;
422 }
423 
424 /**
425   @brief Get a boolean value indicating if the CeedOperator is composite
426 
427   @param[in]  op           CeedOperator
428   @param[out] is_composite Variable to store composite status
429 
430   @return An error code: 0 - success, otherwise - failure
431 
432   @ref Backend
433 **/
434 int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) {
435   *is_composite = op->is_composite;
436   return CEED_ERROR_SUCCESS;
437 }
438 
439 /**
440   @brief Get the backend data of a CeedOperator
441 
442   @param[in]  op   CeedOperator
443   @param[out] data Variable to store data
444 
445   @return An error code: 0 - success, otherwise - failure
446 
447   @ref Backend
448 **/
449 int CeedOperatorGetData(CeedOperator op, void *data) {
450   *(void **)data = op->data;
451   return CEED_ERROR_SUCCESS;
452 }
453 
454 /**
455   @brief Set the backend data of a CeedOperator
456 
457   @param[in,out] op   CeedOperator
458   @param[in]     data Data to set
459 
460   @return An error code: 0 - success, otherwise - failure
461 
462   @ref Backend
463 **/
464 int CeedOperatorSetData(CeedOperator op, void *data) {
465   op->data = data;
466   return CEED_ERROR_SUCCESS;
467 }
468 
469 /**
470   @brief Increment the reference counter for a CeedOperator
471 
472   @param[in,out] op CeedOperator to increment the reference counter
473 
474   @return An error code: 0 - success, otherwise - failure
475 
476   @ref Backend
477 **/
478 int CeedOperatorReference(CeedOperator op) {
479   op->ref_count++;
480   return CEED_ERROR_SUCCESS;
481 }
482 
483 /**
484   @brief Set the setup flag of a CeedOperator to True
485 
486   @param[in,out] op CeedOperator
487 
488   @return An error code: 0 - success, otherwise - failure
489 
490   @ref Backend
491 **/
492 int CeedOperatorSetSetupDone(CeedOperator op) {
493   op->is_backend_setup = true;
494   return CEED_ERROR_SUCCESS;
495 }
496 
497 /// @}
498 
499 /// ----------------------------------------------------------------------------
500 /// CeedOperator Public API
501 /// ----------------------------------------------------------------------------
502 /// @addtogroup CeedOperatorUser
503 /// @{
504 
505 /**
506   @brief Create a CeedOperator and associate a CeedQFunction.
507 
508   A CeedBasis and CeedElemRestriction can be associated with CeedQFunction fields with \ref CeedOperatorSetField.
509 
510   @param[in]  ceed Ceed object where the CeedOperator will be created
511   @param[in]  qf   QFunction defining the action of the operator at quadrature points
512   @param[in]  dqf  QFunction defining the action of the Jacobian of @a qf (or @ref CEED_QFUNCTION_NONE)
513   @param[in]  dqfT QFunction defining the action of the transpose of the Jacobian of @a qf (or @ref CEED_QFUNCTION_NONE)
514   @param[out] op   Address of the variable where the newly created CeedOperator will be stored
515 
516   @return An error code: 0 - success, otherwise - failure
517 
518   @ref User
519  */
520 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf, CeedQFunction dqfT, CeedOperator *op) {
521   if (!ceed->OperatorCreate) {
522     Ceed delegate;
523 
524     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Operator"));
525     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support OperatorCreate");
526     CeedCall(CeedOperatorCreate(delegate, qf, dqf, dqfT, op));
527     return CEED_ERROR_SUCCESS;
528   }
529 
530   CeedCheck(qf && qf != CEED_QFUNCTION_NONE, ceed, CEED_ERROR_MINOR, "Operator must have a valid QFunction.");
531 
532   CeedCall(CeedCalloc(1, op));
533   CeedCall(CeedReferenceCopy(ceed, &(*op)->ceed));
534   (*op)->ref_count   = 1;
535   (*op)->input_size  = -1;
536   (*op)->output_size = -1;
537   CeedCall(CeedQFunctionReferenceCopy(qf, &(*op)->qf));
538   if (dqf && dqf != CEED_QFUNCTION_NONE) CeedCall(CeedQFunctionReferenceCopy(dqf, &(*op)->dqf));
539   if (dqfT && dqfT != CEED_QFUNCTION_NONE) CeedCall(CeedQFunctionReferenceCopy(dqfT, &(*op)->dqfT));
540   CeedCall(CeedQFunctionAssemblyDataCreate(ceed, &(*op)->qf_assembled));
541   CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->input_fields));
542   CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->output_fields));
543   CeedCall(ceed->OperatorCreate(*op));
544   return CEED_ERROR_SUCCESS;
545 }
546 
547 /**
548   @brief Create an operator that composes the action of several operators
549 
550   @param[in]  ceed Ceed object where the CeedOperator will be created
551   @param[out] op   Address of the variable where the newly created Composite CeedOperator will be stored
552 
553   @return An error code: 0 - success, otherwise - failure
554 
555   @ref User
556  */
557 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
558   if (!ceed->CompositeOperatorCreate) {
559     Ceed delegate;
560 
561     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Operator"));
562     if (delegate) {
563       CeedCall(CeedCompositeOperatorCreate(delegate, op));
564       return CEED_ERROR_SUCCESS;
565     }
566   }
567 
568   CeedCall(CeedCalloc(1, op));
569   CeedCall(CeedReferenceCopy(ceed, &(*op)->ceed));
570   (*op)->ref_count    = 1;
571   (*op)->is_composite = true;
572   CeedCall(CeedCalloc(CEED_COMPOSITE_MAX, &(*op)->sub_operators));
573   (*op)->input_size  = -1;
574   (*op)->output_size = -1;
575 
576   if (ceed->CompositeOperatorCreate) CeedCall(ceed->CompositeOperatorCreate(*op));
577   return CEED_ERROR_SUCCESS;
578 }
579 
580 /**
581   @brief Copy the pointer to a CeedOperator.
582 
583   Both pointers should be destroyed with `CeedOperatorDestroy()`.
584 
585   Note: If the value of `op_copy` passed to this function is non-NULL, then it is assumed that `op_copy` is a pointer to a CeedOperator.
586         This CeedOperator will be destroyed if `op_copy` is the only reference to this CeedOperator.
587 
588   @param[in]  op         CeedOperator to copy reference to
589   @param[in,out] op_copy Variable to store copied reference
590 
591   @return An error code: 0 - success, otherwise - failure
592 
593   @ref User
594 **/
595 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
596   CeedCall(CeedOperatorReference(op));
597   CeedCall(CeedOperatorDestroy(op_copy));
598   *op_copy = op;
599   return CEED_ERROR_SUCCESS;
600 }
601 
602 /**
603   @brief Provide a field to a CeedOperator for use by its CeedQFunction.
604 
605   This function is used to specify both active and passive fields to a CeedOperator.
606   For passive fields, a vector @arg v must be provided.
607   Passive fields can inputs or outputs (updated in-place when operator is applied).
608 
609   Active fields must be specified using this function, but their data (in a CeedVector) is passed in CeedOperatorApply().
610   There can be at most one active input CeedVector and at most one active output CeedVector passed to CeedOperatorApply().
611 
612   The number of quadrature points must agree across all points.
613   When using @ref CEED_BASIS_NONE, the number of quadrature points is determined by the element size of r.
614 
615   @param[in,out] op         CeedOperator on which to provide the field
616   @param[in]     field_name Name of the field (to be matched with the name used by CeedQFunction)
617   @param[in]     r          CeedElemRestriction
618   @param[in]     b          CeedBasis in which the field resides or @ref CEED_BASIS_NONE if collocated with quadrature points
619   @param[in]     v          CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE if field is active or @ref CEED_VECTOR_NONE
620                               if using @ref CEED_EVAL_WEIGHT in the QFunction
621 
622   @return An error code: 0 - success, otherwise - failure
623 
624   @ref User
625 **/
626 int CeedOperatorSetField(CeedOperator op, const char *field_name, CeedElemRestriction r, CeedBasis b, CeedVector v) {
627   bool               is_input = true;
628   CeedInt            num_elem = 0, num_qpts = 0;
629   CeedQFunctionField qf_field;
630   CeedOperatorField *op_field;
631 
632   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot add field to composite operator.");
633   CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
634   CeedCheck(r, op->ceed, CEED_ERROR_INCOMPATIBLE, "ElemRestriction r for field \"%s\" must be non-NULL.", field_name);
635   CeedCheck(b, op->ceed, CEED_ERROR_INCOMPATIBLE, "Basis b for field \"%s\" must be non-NULL.", field_name);
636   CeedCheck(v, op->ceed, CEED_ERROR_INCOMPATIBLE, "Vector v for field \"%s\" must be non-NULL.", field_name);
637 
638   CeedCall(CeedElemRestrictionGetNumElements(r, &num_elem));
639   CeedCheck(r == CEED_ELEMRESTRICTION_NONE || !op->has_restriction || op->num_elem == num_elem, op->ceed, CEED_ERROR_DIMENSION,
640             "ElemRestriction with %" CeedInt_FMT " elements incompatible with prior %" CeedInt_FMT " elements", num_elem, op->num_elem);
641   {
642     CeedRestrictionType rstr_type;
643 
644     CeedCall(CeedElemRestrictionGetType(r, &rstr_type));
645     CeedCheck(rstr_type != CEED_RESTRICTION_POINTS, op->ceed, CEED_ERROR_UNSUPPORTED,
646               "CeedElemRestrictionAtPoints not supported for standard operator fields");
647   }
648 
649   if (b == CEED_BASIS_NONE) CeedCall(CeedElemRestrictionGetElementSize(r, &num_qpts));
650   else CeedCall(CeedBasisGetNumQuadraturePoints(b, &num_qpts));
651   CeedCheck(op->num_qpts == 0 || op->num_qpts == num_qpts, op->ceed, CEED_ERROR_DIMENSION,
652             "%s must correspond to the same number of quadrature points as previously added Bases. Found %" CeedInt_FMT
653             " quadrature points but expected %" CeedInt_FMT " quadrature points.",
654             b == CEED_BASIS_NONE ? "ElemRestriction" : "Basis", num_qpts, op->num_qpts);
655   for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
656     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
657       qf_field = op->qf->input_fields[i];
658       op_field = &op->input_fields[i];
659       goto found;
660     }
661   }
662   is_input = false;
663   for (CeedInt i = 0; i < op->qf->num_output_fields; i++) {
664     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
665       qf_field = op->qf->output_fields[i];
666       op_field = &op->output_fields[i];
667       goto found;
668     }
669   }
670   // LCOV_EXCL_START
671   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE, "QFunction has no knowledge of field '%s'", field_name);
672   // LCOV_EXCL_STOP
673 found:
674   CeedCall(CeedOperatorCheckField(op->ceed, qf_field, r, b));
675   CeedCall(CeedCalloc(1, op_field));
676 
677   if (v == CEED_VECTOR_ACTIVE) {
678     CeedSize l_size;
679 
680     CeedCall(CeedElemRestrictionGetLVectorSize(r, &l_size));
681     if (is_input) {
682       if (op->input_size == -1) op->input_size = l_size;
683       CeedCheck(l_size == op->input_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size,
684                 op->input_size);
685     } else {
686       if (op->output_size == -1) op->output_size = l_size;
687       CeedCheck(l_size == op->output_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size,
688                 op->output_size);
689     }
690   }
691 
692   CeedCall(CeedVectorReferenceCopy(v, &(*op_field)->vec));
693   CeedCall(CeedElemRestrictionReferenceCopy(r, &(*op_field)->elem_rstr));
694   if (r != CEED_ELEMRESTRICTION_NONE) {
695     op->num_elem        = num_elem;
696     op->has_restriction = true;  // Restriction set, but num_elem may be 0
697   }
698   CeedCall(CeedBasisReferenceCopy(b, &(*op_field)->basis));
699   if (op->num_qpts == 0) CeedCall(CeedOperatorSetNumQuadraturePoints(op, num_qpts));
700 
701   op->num_fields += 1;
702   CeedCall(CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name));
703   return CEED_ERROR_SUCCESS;
704 }
705 
706 /**
707   @brief Get the CeedOperatorFields of a CeedOperator
708 
709   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
710 
711   @param[in]  op                CeedOperator
712   @param[out] num_input_fields  Variable to store number of input fields
713   @param[out] input_fields      Variable to store input_fields
714   @param[out] num_output_fields Variable to store number of output fields
715   @param[out] output_fields     Variable to store output_fields
716 
717   @return An error code: 0 - success, otherwise - failure
718 
719   @ref Advanced
720 **/
721 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields, CeedOperatorField **input_fields, CeedInt *num_output_fields,
722                           CeedOperatorField **output_fields) {
723   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
724   CeedCall(CeedOperatorCheckReady(op));
725 
726   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
727   if (input_fields) *input_fields = op->input_fields;
728   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
729   if (output_fields) *output_fields = op->output_fields;
730   return CEED_ERROR_SUCCESS;
731 }
732 
733 /**
734   @brief Get a CeedOperatorField of an CeedOperator from its name
735 
736   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
737 
738   @param[in]  op         CeedOperator
739   @param[in]  field_name Name of desired CeedOperatorField
740   @param[out] op_field   CeedOperatorField corresponding to the name
741 
742   @return An error code: 0 - success, otherwise - failure
743 
744   @ref Advanced
745 **/
746 int CeedOperatorGetFieldByName(CeedOperator op, const char *field_name, CeedOperatorField *op_field) {
747   char              *name;
748   CeedInt            num_input_fields, num_output_fields;
749   CeedOperatorField *input_fields, *output_fields;
750 
751   CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields));
752   for (CeedInt i = 0; i < num_input_fields; i++) {
753     CeedCall(CeedOperatorFieldGetName(input_fields[i], &name));
754     if (!strcmp(name, field_name)) {
755       *op_field = input_fields[i];
756       return CEED_ERROR_SUCCESS;
757     }
758   }
759   for (CeedInt i = 0; i < num_output_fields; i++) {
760     CeedCall(CeedOperatorFieldGetName(output_fields[i], &name));
761     if (!strcmp(name, field_name)) {
762       *op_field = output_fields[i];
763       return CEED_ERROR_SUCCESS;
764     }
765   }
766   // LCOV_EXCL_START
767   bool has_name = op->name;
768 
769   return CeedError(op->ceed, CEED_ERROR_MINOR, "The field \"%s\" not found in CeedOperator%s%s%s.\n", field_name, has_name ? " \"" : "",
770                    has_name ? op->name : "", has_name ? "\"" : "");
771   // LCOV_EXCL_STOP
772 }
773 
774 /**
775   @brief Get the name of a CeedOperatorField
776 
777   @param[in]  op_field    CeedOperatorField
778   @param[out] field_name  Variable to store the field name
779 
780   @return An error code: 0 - success, otherwise - failure
781 
782   @ref Advanced
783 **/
784 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
785   *field_name = (char *)op_field->field_name;
786   return CEED_ERROR_SUCCESS;
787 }
788 
789 /**
790   @brief Get the CeedElemRestriction of a CeedOperatorField
791 
792   @param[in]  op_field CeedOperatorField
793   @param[out] rstr     Variable to store CeedElemRestriction
794 
795   @return An error code: 0 - success, otherwise - failure
796 
797   @ref Advanced
798 **/
799 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field, CeedElemRestriction *rstr) {
800   *rstr = op_field->elem_rstr;
801   return CEED_ERROR_SUCCESS;
802 }
803 
804 /**
805   @brief Get the CeedBasis of a CeedOperatorField
806 
807   @param[in]  op_field CeedOperatorField
808   @param[out] basis    Variable to store CeedBasis
809 
810   @return An error code: 0 - success, otherwise - failure
811 
812   @ref Advanced
813 **/
814 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
815   *basis = op_field->basis;
816   return CEED_ERROR_SUCCESS;
817 }
818 
819 /**
820   @brief Get the CeedVector of a CeedOperatorField
821 
822   @param[in]  op_field CeedOperatorField
823   @param[out] vec      Variable to store CeedVector
824 
825   @return An error code: 0 - success, otherwise - failure
826 
827   @ref Advanced
828 **/
829 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
830   *vec = op_field->vec;
831   return CEED_ERROR_SUCCESS;
832 }
833 
834 /**
835   @brief Add a sub-operator to a composite CeedOperator
836 
837   @param[in,out] composite_op Composite CeedOperator
838   @param[in]     sub_op       Sub-operator CeedOperator
839 
840   @return An error code: 0 - success, otherwise - failure
841 
842   @ref User
843  */
844 int CeedCompositeOperatorAddSub(CeedOperator composite_op, CeedOperator sub_op) {
845   CeedCheck(composite_op->is_composite, composite_op->ceed, CEED_ERROR_MINOR, "CeedOperator is not a composite operator");
846   CeedCheck(composite_op->num_suboperators < CEED_COMPOSITE_MAX, composite_op->ceed, CEED_ERROR_UNSUPPORTED, "Cannot add additional sub-operators");
847   CeedCheck(!composite_op->is_immutable, composite_op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
848 
849   {
850     CeedSize input_size, output_size;
851 
852     CeedCall(CeedOperatorGetActiveVectorLengths(sub_op, &input_size, &output_size));
853     if (composite_op->input_size == -1) composite_op->input_size = input_size;
854     if (composite_op->output_size == -1) composite_op->output_size = output_size;
855     // Note, a size of -1 means no active vector restriction set, so no incompatibility
856     CeedCheck((input_size == -1 || input_size == composite_op->input_size) && (output_size == -1 || output_size == composite_op->output_size),
857               composite_op->ceed, CEED_ERROR_MAJOR,
858               "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of "
859               "shape (%td, %td)",
860               composite_op->input_size, composite_op->output_size, input_size, output_size);
861   }
862 
863   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
864   CeedCall(CeedOperatorReference(sub_op));
865   composite_op->num_suboperators++;
866   return CEED_ERROR_SUCCESS;
867 }
868 
869 /**
870   @brief Get the number of sub_operators associated with a CeedOperator
871 
872   @param[in]  op               CeedOperator
873   @param[out] num_suboperators Variable to store number of sub_operators
874 
875   @return An error code: 0 - success, otherwise - failure
876 
877   @ref Backend
878 **/
879 int CeedCompositeOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
880   CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator");
881   *num_suboperators = op->num_suboperators;
882   return CEED_ERROR_SUCCESS;
883 }
884 
885 /**
886   @brief Get the list of sub_operators associated with a CeedOperator
887 
888   @param op                  CeedOperator
889   @param[out] sub_operators  Variable to store list of sub_operators
890 
891   @return An error code: 0 - success, otherwise - failure
892 
893   @ref Backend
894 **/
895 int CeedCompositeOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
896   CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator");
897   *sub_operators = op->sub_operators;
898   return CEED_ERROR_SUCCESS;
899 }
900 
901 /**
902   @brief Check if a CeedOperator is ready to be used.
903 
904   @param[in] op CeedOperator to check
905 
906   @return An error code: 0 - success, otherwise - failure
907 
908   @ref User
909 **/
910 int CeedOperatorCheckReady(CeedOperator op) {
911   Ceed ceed;
912 
913   CeedCall(CeedOperatorGetCeed(op, &ceed));
914 
915   if (op->is_interface_setup) return CEED_ERROR_SUCCESS;
916 
917   CeedQFunction qf = op->qf;
918   if (op->is_composite) {
919     if (!op->num_suboperators) {
920       // Empty operator setup
921       op->input_size  = 0;
922       op->output_size = 0;
923     } else {
924       for (CeedInt i = 0; i < op->num_suboperators; i++) {
925         CeedCall(CeedOperatorCheckReady(op->sub_operators[i]));
926       }
927       // Sub-operators could be modified after adding to composite operator
928       // Need to verify no lvec incompatibility from any changes
929       CeedSize input_size, output_size;
930       CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
931     }
932   } else {
933     CeedCheck(op->num_fields > 0, ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
934     CeedCheck(op->num_fields == qf->num_input_fields + qf->num_output_fields, ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
935     CeedCheck(op->has_restriction, ceed, CEED_ERROR_INCOMPLETE, "At least one restriction required");
936     CeedCheck(op->num_qpts > 0, ceed, CEED_ERROR_INCOMPLETE,
937               "At least one non-collocated basis is required or the number of quadrature points must be set");
938   }
939 
940   // Flag as immutable and ready
941   op->is_interface_setup = true;
942   if (op->qf && op->qf != CEED_QFUNCTION_NONE) op->qf->is_immutable = true;
943   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE) op->dqf->is_immutable = true;
944   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE) op->dqfT->is_immutable = true;
945   return CEED_ERROR_SUCCESS;
946 }
947 
948 /**
949   @brief Get vector lengths for the active input and/or output vectors of a CeedOperator.
950 
951   Note: Lengths of -1 indicate that the CeedOperator does not have an active input and/or output.
952 
953   @param[in]  op          CeedOperator
954   @param[out] input_size  Variable to store active input vector length, or NULL
955   @param[out] output_size Variable to store active output vector length, or NULL
956 
957   @return An error code: 0 - success, otherwise - failure
958 
959   @ref User
960 **/
961 int CeedOperatorGetActiveVectorLengths(CeedOperator op, CeedSize *input_size, CeedSize *output_size) {
962   bool is_composite;
963 
964   if (input_size) *input_size = op->input_size;
965   if (output_size) *output_size = op->output_size;
966 
967   CeedCall(CeedOperatorIsComposite(op, &is_composite));
968   if (is_composite && (op->input_size == -1 || op->output_size == -1)) {
969     for (CeedInt i = 0; i < op->num_suboperators; i++) {
970       CeedSize sub_input_size, sub_output_size;
971 
972       CeedCall(CeedOperatorGetActiveVectorLengths(op->sub_operators[i], &sub_input_size, &sub_output_size));
973       if (op->input_size == -1) op->input_size = sub_input_size;
974       if (op->output_size == -1) op->output_size = sub_output_size;
975       // Note, a size of -1 means no active vector restriction set, so no incompatibility
976       CeedCheck((sub_input_size == -1 || sub_input_size == op->input_size) && (sub_output_size == -1 || sub_output_size == op->output_size), op->ceed,
977                 CEED_ERROR_MAJOR,
978                 "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of "
979                 "shape (%td, %td)",
980                 op->input_size, op->output_size, input_size, output_size);
981     }
982   }
983   return CEED_ERROR_SUCCESS;
984 }
985 
986 /**
987   @brief Set reuse of CeedQFunction data in CeedOperatorLinearAssemble* functions.
988 
989   When `reuse_assembly_data = false` (default), the CeedQFunction associated with this CeedOperator is re-assembled every time a
990 `CeedOperatorLinearAssemble*` function is called.
991   When `reuse_assembly_data = true`, the CeedQFunction associated with this CeedOperator is reused between calls to
992 `CeedOperatorSetQFunctionAssemblyDataUpdated`.
993 
994   @param[in] op                  CeedOperator
995   @param[in] reuse_assembly_data Boolean flag setting assembly data reuse
996 
997   @return An error code: 0 - success, otherwise - failure
998 
999   @ref Advanced
1000 **/
1001 int CeedOperatorSetQFunctionAssemblyReuse(CeedOperator op, bool reuse_assembly_data) {
1002   bool is_composite;
1003 
1004   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1005   if (is_composite) {
1006     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1007       CeedCall(CeedOperatorSetQFunctionAssemblyReuse(op->sub_operators[i], reuse_assembly_data));
1008     }
1009   } else {
1010     CeedCall(CeedQFunctionAssemblyDataSetReuse(op->qf_assembled, reuse_assembly_data));
1011   }
1012   return CEED_ERROR_SUCCESS;
1013 }
1014 
1015 /**
1016   @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly.
1017 
1018   @param[in] op                CeedOperator
1019   @param[in] needs_data_update Boolean flag setting assembly data reuse
1020 
1021   @return An error code: 0 - success, otherwise - failure
1022 
1023   @ref Advanced
1024 **/
1025 int CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(CeedOperator op, bool needs_data_update) {
1026   bool is_composite;
1027 
1028   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1029   if (is_composite) {
1030     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1031       CeedCall(CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(op->sub_operators[i], needs_data_update));
1032     }
1033   } else {
1034     CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, needs_data_update));
1035   }
1036   return CEED_ERROR_SUCCESS;
1037 }
1038 
1039 /**
1040   @brief Set the number of quadrature points associated with a CeedOperator.
1041 
1042   This should be used when creating a CeedOperator where every field has a collocated basis.
1043   This function cannot be used for composite CeedOperators.
1044 
1045   @param[in,out] op       CeedOperator
1046   @param[in]     num_qpts Number of quadrature points to set
1047 
1048   @return An error code: 0 - success, otherwise - failure
1049 
1050   @ref Advanced
1051 **/
1052 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
1053   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite CeedOperator");
1054   CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
1055   if (op->num_qpts > 0) {
1056     CeedWarn(
1057         "CeedOperatorSetNumQuadraturePoints will be removed from the libCEED interface in the next release.\n"
1058         "This function is reduntant and you can safely remove any calls to this function without replacing them.");
1059     CeedCheck(num_qpts == op->num_qpts, op->ceed, CEED_ERROR_DIMENSION, "Different number of quadrature points already defined for the CeedOperator");
1060   }
1061   op->num_qpts = num_qpts;
1062   return CEED_ERROR_SUCCESS;
1063 }
1064 
1065 /**
1066   @brief Set name of CeedOperator for CeedOperatorView output
1067 
1068   @param[in,out] op   CeedOperator
1069   @param[in]     name Name to set, or NULL to remove previously set name
1070 
1071   @return An error code: 0 - success, otherwise - failure
1072 
1073   @ref User
1074 **/
1075 int CeedOperatorSetName(CeedOperator op, const char *name) {
1076   char  *name_copy;
1077   size_t name_len = name ? strlen(name) : 0;
1078 
1079   CeedCall(CeedFree(&op->name));
1080   if (name_len > 0) {
1081     CeedCall(CeedCalloc(name_len + 1, &name_copy));
1082     memcpy(name_copy, name, name_len);
1083     op->name = name_copy;
1084   }
1085   return CEED_ERROR_SUCCESS;
1086 }
1087 
1088 /**
1089   @brief View a CeedOperator
1090 
1091   @param[in] op     CeedOperator to view
1092   @param[in] stream Stream to write; typically stdout/stderr or a file
1093 
1094   @return Error code: 0 - success, otherwise - failure
1095 
1096   @ref User
1097 **/
1098 int CeedOperatorView(CeedOperator op, FILE *stream) {
1099   bool has_name = op->name;
1100 
1101   if (op->is_composite) {
1102     fprintf(stream, "Composite CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : "");
1103 
1104     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1105       has_name = op->sub_operators[i]->name;
1106       fprintf(stream, "  SubOperator %" CeedInt_FMT "%s%s:\n", i, has_name ? " - " : "", has_name ? op->sub_operators[i]->name : "");
1107       CeedCall(CeedOperatorSingleView(op->sub_operators[i], 1, stream));
1108     }
1109   } else {
1110     fprintf(stream, "CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : "");
1111     CeedCall(CeedOperatorSingleView(op, 0, stream));
1112   }
1113   return CEED_ERROR_SUCCESS;
1114 }
1115 
1116 /**
1117   @brief Get the Ceed associated with a CeedOperator
1118 
1119   @param[in]  op   CeedOperator
1120   @param[out] ceed Variable to store Ceed
1121 
1122   @return An error code: 0 - success, otherwise - failure
1123 
1124   @ref Advanced
1125 **/
1126 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
1127   *ceed = op->ceed;
1128   return CEED_ERROR_SUCCESS;
1129 }
1130 
1131 /**
1132   @brief Get the number of elements associated with a CeedOperator
1133 
1134   @param[in]  op       CeedOperator
1135   @param[out] num_elem Variable to store number of elements
1136 
1137   @return An error code: 0 - success, otherwise - failure
1138 
1139   @ref Advanced
1140 **/
1141 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1142   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
1143   *num_elem = op->num_elem;
1144   return CEED_ERROR_SUCCESS;
1145 }
1146 
1147 /**
1148   @brief Get the number of quadrature points associated with a CeedOperator
1149 
1150   @param[in]  op       CeedOperator
1151   @param[out] num_qpts Variable to store vector number of quadrature points
1152 
1153   @return An error code: 0 - success, otherwise - failure
1154 
1155   @ref Advanced
1156 **/
1157 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1158   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
1159   *num_qpts = op->num_qpts;
1160   return CEED_ERROR_SUCCESS;
1161 }
1162 
1163 /**
1164   @brief Estimate number of FLOPs required to apply CeedOperator on the active vector
1165 
1166   @param[in]  op    CeedOperator to estimate FLOPs for
1167   @param[out] flops Address of variable to hold FLOPs estimate
1168 
1169   @ref Backend
1170 **/
1171 int CeedOperatorGetFlopsEstimate(CeedOperator op, CeedSize *flops) {
1172   bool is_composite;
1173 
1174   CeedCall(CeedOperatorCheckReady(op));
1175 
1176   *flops = 0;
1177   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1178   if (is_composite) {
1179     CeedInt num_suboperators;
1180 
1181     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1182     CeedOperator *sub_operators;
1183     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1184 
1185     // FLOPs for each suboperator
1186     for (CeedInt i = 0; i < num_suboperators; i++) {
1187       CeedSize suboperator_flops;
1188 
1189       CeedCall(CeedOperatorGetFlopsEstimate(sub_operators[i], &suboperator_flops));
1190       *flops += suboperator_flops;
1191     }
1192   } else {
1193     CeedInt            num_input_fields, num_output_fields, num_elem = 0;
1194     CeedOperatorField *input_fields, *output_fields;
1195 
1196     CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields));
1197     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
1198 
1199     // Input FLOPs
1200     for (CeedInt i = 0; i < num_input_fields; i++) {
1201       if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1202         CeedSize rstr_flops, basis_flops;
1203 
1204         CeedCall(CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_rstr, CEED_NOTRANSPOSE, &rstr_flops));
1205         *flops += rstr_flops;
1206         CeedCall(CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE, op->qf->input_fields[i]->eval_mode, &basis_flops));
1207         *flops += basis_flops * num_elem;
1208       }
1209     }
1210     // QF FLOPs
1211     {
1212       CeedInt  num_qpts;
1213       CeedSize qf_flops;
1214 
1215       CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts));
1216       CeedCall(CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops));
1217       *flops += num_elem * num_qpts * qf_flops;
1218     }
1219 
1220     // Output FLOPs
1221     for (CeedInt i = 0; i < num_output_fields; i++) {
1222       if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1223         CeedSize rstr_flops, basis_flops;
1224 
1225         CeedCall(CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_rstr, CEED_TRANSPOSE, &rstr_flops));
1226         *flops += rstr_flops;
1227         CeedCall(CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE, op->qf->output_fields[i]->eval_mode, &basis_flops));
1228         *flops += basis_flops * num_elem;
1229       }
1230     }
1231   }
1232   return CEED_ERROR_SUCCESS;
1233 }
1234 
1235 /**
1236   @brief Get CeedQFunction global context for a CeedOperator.
1237 
1238   The caller is responsible for destroying `ctx` returned from this function via `CeedQFunctionContextDestroy()`.
1239 
1240   Note: If the value of `ctx` passed into this function is non-NULL, then it is assumed that `ctx` is a pointer to a CeedQFunctionContext.
1241         This CeedQFunctionContext will be destroyed if `ctx` is the only reference to this CeedQFunctionContext.
1242 
1243   @param[in]  op  CeedOperator
1244   @param[out] ctx Variable to store CeedQFunctionContext
1245 
1246   @return An error code: 0 - success, otherwise - failure
1247 
1248   @ref Advanced
1249 **/
1250 int CeedOperatorGetContext(CeedOperator op, CeedQFunctionContext *ctx) {
1251   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot retrieve QFunctionContext for composite operator");
1252   if (op->qf->ctx) CeedCall(CeedQFunctionContextReferenceCopy(op->qf->ctx, ctx));
1253   else *ctx = NULL;
1254   return CEED_ERROR_SUCCESS;
1255 }
1256 
1257 /**
1258   @brief Get label for a registered QFunctionContext field, or `NULL` if no field has been registered with this `field_name`.
1259 
1260   Fields are registered via `CeedQFunctionContextRegister*()` functions (eg. `CeedQFunctionContextRegisterDouble()`).
1261 
1262   @param[in]  op          CeedOperator
1263   @param[in]  field_name  Name of field to retrieve label
1264   @param[out] field_label Variable to field label
1265 
1266   @return An error code: 0 - success, otherwise - failure
1267 
1268   @ref User
1269 **/
1270 int CeedOperatorGetContextFieldLabel(CeedOperator op, const char *field_name, CeedContextFieldLabel *field_label) {
1271   bool is_composite, field_found = false;
1272 
1273   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1274 
1275   if (is_composite) {
1276     // Composite operator
1277     // -- Check if composite label already created
1278     for (CeedInt i = 0; i < op->num_context_labels; i++) {
1279       if (!strcmp(op->context_labels[i]->name, field_name)) {
1280         *field_label = op->context_labels[i];
1281         return CEED_ERROR_SUCCESS;
1282       }
1283     }
1284 
1285     // -- Create composite label if needed
1286     CeedInt               num_sub;
1287     CeedOperator         *sub_operators;
1288     CeedContextFieldLabel new_field_label;
1289 
1290     CeedCall(CeedCalloc(1, &new_field_label));
1291     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
1292     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1293     CeedCall(CeedCalloc(num_sub, &new_field_label->sub_labels));
1294     new_field_label->num_sub_labels = num_sub;
1295 
1296     for (CeedInt i = 0; i < num_sub; i++) {
1297       if (sub_operators[i]->qf->ctx) {
1298         CeedContextFieldLabel new_field_label_i;
1299 
1300         CeedCall(CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name, &new_field_label_i));
1301         if (new_field_label_i) {
1302           field_found                    = true;
1303           new_field_label->sub_labels[i] = new_field_label_i;
1304           new_field_label->name          = new_field_label_i->name;
1305           new_field_label->description   = new_field_label_i->description;
1306           if (new_field_label->type && new_field_label->type != new_field_label_i->type) {
1307             // LCOV_EXCL_START
1308             CeedCall(CeedFree(&new_field_label));
1309             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field types on sub-operator contexts. %s != %s",
1310                              CeedContextFieldTypes[new_field_label->type], CeedContextFieldTypes[new_field_label_i->type]);
1311             // LCOV_EXCL_STOP
1312           } else {
1313             new_field_label->type = new_field_label_i->type;
1314           }
1315           if (new_field_label->num_values != 0 && new_field_label->num_values != new_field_label_i->num_values) {
1316             // LCOV_EXCL_START
1317             CeedCall(CeedFree(&new_field_label));
1318             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field number of values on sub-operator contexts. %ld != %ld",
1319                              new_field_label->num_values, new_field_label_i->num_values);
1320             // LCOV_EXCL_STOP
1321           } else {
1322             new_field_label->num_values = new_field_label_i->num_values;
1323           }
1324         }
1325       }
1326     }
1327     // -- Cleanup if field was found
1328     if (field_found) {
1329       *field_label = new_field_label;
1330     } else {
1331       // LCOV_EXCL_START
1332       CeedCall(CeedFree(&new_field_label->sub_labels));
1333       CeedCall(CeedFree(&new_field_label));
1334       *field_label = NULL;
1335       // LCOV_EXCL_STOP
1336     }
1337   } else {
1338     // Single, non-composite operator
1339     if (op->qf->ctx) {
1340       CeedCall(CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label));
1341     } else {
1342       *field_label = NULL;
1343     }
1344   }
1345 
1346   // Set label in operator
1347   if (*field_label) {
1348     (*field_label)->from_op = true;
1349 
1350     // Move new composite label to operator
1351     if (op->num_context_labels == 0) {
1352       CeedCall(CeedCalloc(1, &op->context_labels));
1353       op->max_context_labels = 1;
1354     } else if (op->num_context_labels == op->max_context_labels) {
1355       CeedCall(CeedRealloc(2 * op->num_context_labels, &op->context_labels));
1356       op->max_context_labels *= 2;
1357     }
1358     op->context_labels[op->num_context_labels] = *field_label;
1359     op->num_context_labels++;
1360   }
1361   return CEED_ERROR_SUCCESS;
1362 }
1363 
1364 /**
1365   @brief Set QFunctionContext field holding double precision values.
1366 
1367   For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`.
1368 
1369   @param[in,out] op          CeedOperator
1370   @param[in]     field_label Label of field to set
1371   @param[in]     values      Values to set
1372 
1373   @return An error code: 0 - success, otherwise - failure
1374 
1375   @ref User
1376 **/
1377 int CeedOperatorSetContextDouble(CeedOperator op, CeedContextFieldLabel field_label, double *values) {
1378   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values);
1379 }
1380 
1381 /**
1382   @brief Get QFunctionContext field holding double precision values, read-only.
1383 
1384   For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`.
1385 
1386   @param[in]  op          CeedOperator
1387   @param[in]  field_label Label of field to get
1388   @param[out] num_values  Number of values in the field label
1389   @param[out] values      Pointer to context values
1390 
1391   @return An error code: 0 - success, otherwise - failure
1392 
1393   @ref User
1394 **/
1395 int CeedOperatorGetContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const double **values) {
1396   return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, num_values, values);
1397 }
1398 
1399 /**
1400   @brief Restore QFunctionContext field holding double precision values, read-only.
1401 
1402   @param[in]  op          CeedOperator
1403   @param[in]  field_label Label of field to restore
1404   @param[out] values      Pointer to context values
1405 
1406   @return An error code: 0 - success, otherwise - failure
1407 
1408   @ref User
1409 **/
1410 int CeedOperatorRestoreContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, const double **values) {
1411   return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values);
1412 }
1413 
1414 /**
1415   @brief Set QFunctionContext field holding int32 values.
1416 
1417   For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`.
1418 
1419   @param[in,out] op          CeedOperator
1420   @param[in]     field_label Label of field to set
1421   @param[in]     values      Values to set
1422 
1423   @return An error code: 0 - success, otherwise - failure
1424 
1425   @ref User
1426 **/
1427 int CeedOperatorSetContextInt32(CeedOperator op, CeedContextFieldLabel field_label, int *values) {
1428   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32, values);
1429 }
1430 
1431 /**
1432   @brief Get QFunctionContext field holding int32 values, read-only.
1433 
1434   For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`.
1435 
1436   @param[in]  op          CeedOperator
1437   @param[in]  field_label Label of field to get
1438   @param[out] num_values  Number of int32 values in `values`
1439   @param[out] values      Pointer to context values
1440 
1441   @return An error code: 0 - success, otherwise - failure
1442 
1443   @ref User
1444 **/
1445 int CeedOperatorGetContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const int **values) {
1446   return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, num_values, values);
1447 }
1448 
1449 /**
1450   @brief Restore QFunctionContext field holding int32 values, read-only.
1451 
1452   @param[in]  op          CeedOperator
1453   @param[in]  field_label Label of field to get
1454   @param[out] values      Pointer to context values
1455 
1456   @return An error code: 0 - success, otherwise - failure
1457 
1458   @ref User
1459 **/
1460 int CeedOperatorRestoreContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, const int **values) {
1461   return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, values);
1462 }
1463 
1464 /**
1465   @brief Apply CeedOperator to a vector
1466 
1467   This computes the action of the operator on the specified (active) input, yielding its (active) output.
1468   All inputs and outputs must be specified using CeedOperatorSetField().
1469 
1470   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1471 
1472   @param[in]  op      CeedOperator to apply
1473   @param[in]  in      CeedVector containing input state or @ref CEED_VECTOR_NONE if there are no active inputs
1474   @param[out] out     CeedVector to store result of applying operator (must be distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1475 active outputs
1476   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1477 
1478   @return An error code: 0 - success, otherwise - failure
1479 
1480   @ref User
1481 **/
1482 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) {
1483   CeedCall(CeedOperatorCheckReady(op));
1484 
1485   if (op->is_composite) {
1486     // Composite Operator
1487     if (op->ApplyComposite) {
1488       CeedCall(op->ApplyComposite(op, in, out, request));
1489     } else {
1490       CeedInt       num_suboperators;
1491       CeedOperator *sub_operators;
1492 
1493       CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1494       CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1495 
1496       // Zero all output vectors
1497       if (out != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(out, 0.0));
1498       for (CeedInt i = 0; i < num_suboperators; i++) {
1499         for (CeedInt j = 0; j < sub_operators[i]->qf->num_output_fields; j++) {
1500           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1501 
1502           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1503             CeedCall(CeedVectorSetValue(vec, 0.0));
1504           }
1505         }
1506       }
1507       // Apply
1508       for (CeedInt i = 0; i < num_suboperators; i++) {
1509         CeedCall(CeedOperatorApplyAdd(sub_operators[i], in, out, request));
1510       }
1511     }
1512   } else {
1513     // Standard Operator
1514     if (op->Apply) {
1515       CeedCall(op->Apply(op, in, out, request));
1516     } else {
1517       // Zero all output vectors
1518       CeedQFunction qf = op->qf;
1519 
1520       for (CeedInt i = 0; i < qf->num_output_fields; i++) {
1521         CeedVector vec = op->output_fields[i]->vec;
1522 
1523         if (vec == CEED_VECTOR_ACTIVE) vec = out;
1524         if (vec != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(vec, 0.0));
1525       }
1526       // Apply
1527       if (op->num_elem) CeedCall(op->ApplyAdd(op, in, out, request));
1528     }
1529   }
1530   return CEED_ERROR_SUCCESS;
1531 }
1532 
1533 /**
1534   @brief Apply CeedOperator to a vector and add result to output vector
1535 
1536   This computes the action of the operator on the specified (active) input, yielding its (active) output.
1537   All inputs and outputs must be specified using CeedOperatorSetField().
1538 
1539   @param[in]  op      CeedOperator to apply
1540   @param[in]  in      CeedVector containing input state or NULL if there are no active inputs
1541   @param[out] out     CeedVector to sum in result of applying operator (must be distinct from @a in) or NULL if there are no active outputs
1542   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1543 
1544   @return An error code: 0 - success, otherwise - failure
1545 
1546   @ref User
1547 **/
1548 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) {
1549   CeedCall(CeedOperatorCheckReady(op));
1550 
1551   if (op->is_composite) {
1552     // Composite Operator
1553     if (op->ApplyAddComposite) {
1554       CeedCall(op->ApplyAddComposite(op, in, out, request));
1555     } else {
1556       CeedInt       num_suboperators;
1557       CeedOperator *sub_operators;
1558 
1559       CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1560       CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1561       for (CeedInt i = 0; i < num_suboperators; i++) {
1562         CeedCall(CeedOperatorApplyAdd(sub_operators[i], in, out, request));
1563       }
1564     }
1565   } else if (op->num_elem) {
1566     // Standard Operator
1567     CeedCall(op->ApplyAdd(op, in, out, request));
1568   }
1569   return CEED_ERROR_SUCCESS;
1570 }
1571 
1572 /**
1573   @brief Destroy a CeedOperator
1574 
1575   @param[in,out] op CeedOperator to destroy
1576 
1577   @return An error code: 0 - success, otherwise - failure
1578 
1579   @ref User
1580 **/
1581 int CeedOperatorDestroy(CeedOperator *op) {
1582   if (!*op || --(*op)->ref_count > 0) {
1583     *op = NULL;
1584     return CEED_ERROR_SUCCESS;
1585   }
1586   if ((*op)->Destroy) CeedCall((*op)->Destroy(*op));
1587   CeedCall(CeedDestroy(&(*op)->ceed));
1588   // Free fields
1589   for (CeedInt i = 0; i < (*op)->num_fields; i++) {
1590     if ((*op)->input_fields[i]) {
1591       if ((*op)->input_fields[i]->elem_rstr != CEED_ELEMRESTRICTION_NONE) {
1592         CeedCall(CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_rstr));
1593       }
1594       if ((*op)->input_fields[i]->basis != CEED_BASIS_NONE) {
1595         CeedCall(CeedBasisDestroy(&(*op)->input_fields[i]->basis));
1596       }
1597       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->input_fields[i]->vec != CEED_VECTOR_NONE) {
1598         CeedCall(CeedVectorDestroy(&(*op)->input_fields[i]->vec));
1599       }
1600       CeedCall(CeedFree(&(*op)->input_fields[i]->field_name));
1601       CeedCall(CeedFree(&(*op)->input_fields[i]));
1602     }
1603   }
1604   for (CeedInt i = 0; i < (*op)->num_fields; i++) {
1605     if ((*op)->output_fields[i]) {
1606       CeedCall(CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_rstr));
1607       if ((*op)->output_fields[i]->basis != CEED_BASIS_NONE) {
1608         CeedCall(CeedBasisDestroy(&(*op)->output_fields[i]->basis));
1609       }
1610       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->output_fields[i]->vec != CEED_VECTOR_NONE) {
1611         CeedCall(CeedVectorDestroy(&(*op)->output_fields[i]->vec));
1612       }
1613       CeedCall(CeedFree(&(*op)->output_fields[i]->field_name));
1614       CeedCall(CeedFree(&(*op)->output_fields[i]));
1615     }
1616   }
1617   // Destroy sub_operators
1618   for (CeedInt i = 0; i < (*op)->num_suboperators; i++) {
1619     if ((*op)->sub_operators[i]) {
1620       CeedCall(CeedOperatorDestroy(&(*op)->sub_operators[i]));
1621     }
1622   }
1623   CeedCall(CeedQFunctionDestroy(&(*op)->qf));
1624   CeedCall(CeedQFunctionDestroy(&(*op)->dqf));
1625   CeedCall(CeedQFunctionDestroy(&(*op)->dqfT));
1626   // Destroy any composite labels
1627   if ((*op)->is_composite) {
1628     for (CeedInt i = 0; i < (*op)->num_context_labels; i++) {
1629       CeedCall(CeedFree(&(*op)->context_labels[i]->sub_labels));
1630       CeedCall(CeedFree(&(*op)->context_labels[i]));
1631     }
1632   }
1633   CeedCall(CeedFree(&(*op)->context_labels));
1634 
1635   // Destroy fallback
1636   CeedCall(CeedOperatorDestroy(&(*op)->op_fallback));
1637 
1638   // Destroy assembly data
1639   CeedCall(CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled));
1640   CeedCall(CeedOperatorAssemblyDataDestroy(&(*op)->op_assembled));
1641 
1642   CeedCall(CeedFree(&(*op)->input_fields));
1643   CeedCall(CeedFree(&(*op)->output_fields));
1644   CeedCall(CeedFree(&(*op)->sub_operators));
1645   CeedCall(CeedFree(&(*op)->name));
1646   CeedCall(CeedFree(op));
1647   return CEED_ERROR_SUCCESS;
1648 }
1649 
1650 /// @}
1651