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