xref: /libCEED/interface/ceed-operator.c (revision 1d2f0dcb59e9d9d08cb71f4dd5a108c5de60a891)
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 
210   For composite operators, the value is set in all sub-operator QFunctionContexts that have a matching `field_name`.
211   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
212 any field of a matching type.
213 
214   @param[in,out] op          CeedOperator
215   @param[in]     field_label Label of field to set
216   @param[in]     field_type  Type of field to set
217   @param[in]     values      Values to set
218 
219   @return An error code: 0 - success, otherwise - failure
220 
221   @ref User
222 **/
223 static int CeedOperatorContextSetGeneric(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, void *values) {
224   CeedCheck(field_label, op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label");
225 
226   // Check if field_label and op correspond
227   if (field_label->from_op) {
228     CeedInt index = -1;
229 
230     for (CeedInt i = 0; i < op->num_context_labels; i++) {
231       if (op->context_labels[i] == field_label) index = i;
232     }
233     CeedCheck(index != -1, op->ceed, CEED_ERROR_UNSUPPORTED, "ContextFieldLabel does not correspond to the operator");
234   }
235 
236   bool is_composite = false;
237   CeedCall(CeedOperatorIsComposite(op, &is_composite));
238   if (is_composite) {
239     CeedInt       num_sub;
240     CeedOperator *sub_operators;
241 
242     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
243     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
244     CeedCheck(num_sub == field_label->num_sub_labels, op->ceed, CEED_ERROR_UNSUPPORTED,
245               "Composite operator modified after ContextFieldLabel created");
246 
247     for (CeedInt i = 0; i < num_sub; i++) {
248       // Try every sub-operator, ok if some sub-operators do not have field
249       if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) {
250         CeedCall(CeedQFunctionContextSetGeneric(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, values));
251       }
252     }
253   } else {
254     CeedCheck(op->qf->ctx, op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data");
255     CeedCall(CeedQFunctionContextSetGeneric(op->qf->ctx, field_label, field_type, values));
256   }
257 
258   return CEED_ERROR_SUCCESS;
259 }
260 
261 /**
262   @brief Get QFunctionContext field values of the specified type, read-only.
263 
264   For composite operators, the values retrieved are for the first sub-operator QFunctionContext that have a matching `field_name`.
265   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
266 any field of a matching type.
267 
268   @param[in,out] op          CeedOperator
269   @param[in]     field_label Label of field to set
270   @param[in]     field_type  Type of field to set
271   @param[out]    num_values  Number of values of type `field_type` in array `values`
272   @param[out]    values      Values in the label
273 
274   @return An error code: 0 - success, otherwise - failure
275 
276   @ref User
277 **/
278 static int CeedOperatorContextGetGenericRead(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, size_t *num_values,
279                                              void *values) {
280   CeedCheck(field_label, op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label");
281 
282   *(void **)values = NULL;
283   *num_values      = 0;
284 
285   // Check if field_label and op correspond
286   if (field_label->from_op) {
287     CeedInt index = -1;
288 
289     for (CeedInt i = 0; i < op->num_context_labels; i++) {
290       if (op->context_labels[i] == field_label) index = i;
291     }
292     CeedCheck(index != -1, op->ceed, CEED_ERROR_UNSUPPORTED, "ContextFieldLabel does not correspond to the operator");
293   }
294 
295   bool is_composite = false;
296   CeedCall(CeedOperatorIsComposite(op, &is_composite));
297   if (is_composite) {
298     CeedInt       num_sub;
299     CeedOperator *sub_operators;
300 
301     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
302     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
303     CeedCheck(num_sub == field_label->num_sub_labels, op->ceed, CEED_ERROR_UNSUPPORTED,
304               "Composite operator modified after ContextFieldLabel created");
305 
306     for (CeedInt i = 0; i < num_sub; i++) {
307       // Try every sub-operator, ok if some sub-operators do not have field
308       if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) {
309         CeedCall(CeedQFunctionContextGetGenericRead(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, num_values, values));
310         return CEED_ERROR_SUCCESS;
311       }
312     }
313   } else {
314     CeedCheck(op->qf->ctx, op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data");
315     CeedCall(CeedQFunctionContextGetGenericRead(op->qf->ctx, field_label, field_type, num_values, values));
316   }
317 
318   return CEED_ERROR_SUCCESS;
319 }
320 
321 /**
322   @brief Restore QFunctionContext field values of the specified type, read-only.
323 
324   For composite operators, the values restored are for the first sub-operator QFunctionContext that have a matching `field_name`.
325   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
326 any field of a matching type.
327 
328   @param[in,out] op          CeedOperator
329   @param[in]     field_label Label of field to set
330   @param[in]     field_type  Type of field to set
331   @param[in]     values      Values array to restore
332 
333   @return An error code: 0 - success, otherwise - failure
334 
335   @ref User
336 **/
337 static int CeedOperatorContextRestoreGenericRead(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, void *values) {
338   CeedCheck(field_label, op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label");
339 
340   // Check if field_label and op correspond
341   if (field_label->from_op) {
342     CeedInt index = -1;
343 
344     for (CeedInt i = 0; i < op->num_context_labels; i++) {
345       if (op->context_labels[i] == field_label) index = i;
346     }
347     CeedCheck(index != -1, op->ceed, CEED_ERROR_UNSUPPORTED, "ContextFieldLabel does not correspond to the operator");
348   }
349 
350   bool is_composite = false;
351   CeedCall(CeedOperatorIsComposite(op, &is_composite));
352   if (is_composite) {
353     CeedInt       num_sub;
354     CeedOperator *sub_operators;
355 
356     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
357     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
358     CeedCheck(num_sub == field_label->num_sub_labels, op->ceed, CEED_ERROR_UNSUPPORTED,
359               "Composite operator modified after ContextFieldLabel created");
360 
361     for (CeedInt i = 0; i < num_sub; i++) {
362       // Try every sub-operator, ok if some sub-operators do not have field
363       if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) {
364         CeedCall(CeedQFunctionContextRestoreGenericRead(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, values));
365         return CEED_ERROR_SUCCESS;
366       }
367     }
368   } else {
369     CeedCheck(op->qf->ctx, op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data");
370     CeedCall(CeedQFunctionContextRestoreGenericRead(op->qf->ctx, field_label, field_type, values));
371   }
372 
373   return CEED_ERROR_SUCCESS;
374 }
375 
376 /// @}
377 
378 /// ----------------------------------------------------------------------------
379 /// CeedOperator Backend API
380 /// ----------------------------------------------------------------------------
381 /// @addtogroup CeedOperatorBackend
382 /// @{
383 
384 /**
385   @brief Get the number of arguments associated with a CeedOperator
386 
387   @param[in]  op        CeedOperator
388   @param[out] num_args  Variable to store vector number of arguments
389 
390   @return An error code: 0 - success, otherwise - failure
391 
392   @ref Backend
393 **/
394 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) {
395   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operators");
396   *num_args = op->num_fields;
397   return CEED_ERROR_SUCCESS;
398 }
399 
400 /**
401   @brief Get the setup status of a CeedOperator
402 
403   @param[in]  op            CeedOperator
404   @param[out] is_setup_done Variable to store setup status
405 
406   @return An error code: 0 - success, otherwise - failure
407 
408   @ref Backend
409 **/
410 int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) {
411   *is_setup_done = op->is_backend_setup;
412   return CEED_ERROR_SUCCESS;
413 }
414 
415 /**
416   @brief Get the QFunction associated with a CeedOperator
417 
418   @param[in]  op CeedOperator
419   @param[out] qf Variable to store QFunction
420 
421   @return An error code: 0 - success, otherwise - failure
422 
423   @ref Backend
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 int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) {
442   *is_composite = op->is_composite;
443   return CEED_ERROR_SUCCESS;
444 }
445 
446 /**
447   @brief Get the backend data of a CeedOperator
448 
449   @param[in]  op   CeedOperator
450   @param[out] data Variable to store data
451 
452   @return An error code: 0 - success, otherwise - failure
453 
454   @ref Backend
455 **/
456 int CeedOperatorGetData(CeedOperator op, void *data) {
457   *(void **)data = op->data;
458   return CEED_ERROR_SUCCESS;
459 }
460 
461 /**
462   @brief Set the backend data of a CeedOperator
463 
464   @param[in,out] op   CeedOperator
465   @param[in]     data Data to set
466 
467   @return An error code: 0 - success, otherwise - failure
468 
469   @ref Backend
470 **/
471 int CeedOperatorSetData(CeedOperator op, void *data) {
472   op->data = data;
473   return CEED_ERROR_SUCCESS;
474 }
475 
476 /**
477   @brief Increment the reference counter for a CeedOperator
478 
479   @param[in,out] op CeedOperator to increment the reference counter
480 
481   @return An error code: 0 - success, otherwise - failure
482 
483   @ref Backend
484 **/
485 int CeedOperatorReference(CeedOperator op) {
486   op->ref_count++;
487   return CEED_ERROR_SUCCESS;
488 }
489 
490 /**
491   @brief Set the setup flag of a CeedOperator to True
492 
493   @param[in,out] op CeedOperator
494 
495   @return An error code: 0 - success, otherwise - failure
496 
497   @ref Backend
498 **/
499 int CeedOperatorSetSetupDone(CeedOperator op) {
500   op->is_backend_setup = true;
501   return CEED_ERROR_SUCCESS;
502 }
503 
504 /// @}
505 
506 /// ----------------------------------------------------------------------------
507 /// CeedOperator Public API
508 /// ----------------------------------------------------------------------------
509 /// @addtogroup CeedOperatorUser
510 /// @{
511 
512 /**
513   @brief Create a CeedOperator and associate a CeedQFunction.
514 
515   A CeedBasis and CeedElemRestriction can be associated with CeedQFunction fields with \ref CeedOperatorSetField.
516 
517   @param[in]  ceed Ceed object where the CeedOperator will be created
518   @param[in]  qf   QFunction defining the action of the operator at quadrature points
519   @param[in]  dqf  QFunction defining the action of the Jacobian of @a qf (or @ref CEED_QFUNCTION_NONE)
520   @param[in]  dqfT QFunction defining the action of the transpose of the Jacobian of @a qf (or @ref CEED_QFUNCTION_NONE)
521   @param[out] op   Address of the variable where the newly created CeedOperator will be stored
522 
523   @return An error code: 0 - success, otherwise - failure
524 
525   @ref User
526  */
527 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf, CeedQFunction dqfT, CeedOperator *op) {
528   if (!ceed->OperatorCreate) {
529     Ceed delegate;
530 
531     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Operator"));
532     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support OperatorCreate");
533     CeedCall(CeedOperatorCreate(delegate, qf, dqf, dqfT, op));
534     return CEED_ERROR_SUCCESS;
535   }
536 
537   CeedCheck(qf && qf != CEED_QFUNCTION_NONE, ceed, CEED_ERROR_MINOR, "Operator must have a valid QFunction.");
538   CeedCall(CeedCalloc(1, op));
539   (*op)->ceed = ceed;
540   CeedCall(CeedReference(ceed));
541   (*op)->ref_count   = 1;
542   (*op)->qf          = qf;
543   (*op)->input_size  = -1;
544   (*op)->output_size = -1;
545   CeedCall(CeedQFunctionReference(qf));
546   if (dqf && dqf != CEED_QFUNCTION_NONE) {
547     (*op)->dqf = dqf;
548     CeedCall(CeedQFunctionReference(dqf));
549   }
550   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
551     (*op)->dqfT = dqfT;
552     CeedCall(CeedQFunctionReference(dqfT));
553   }
554   CeedCall(CeedQFunctionAssemblyDataCreate(ceed, &(*op)->qf_assembled));
555   CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->input_fields));
556   CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->output_fields));
557   CeedCall(ceed->OperatorCreate(*op));
558   return CEED_ERROR_SUCCESS;
559 }
560 
561 /**
562   @brief Create an operator that composes the action of several operators
563 
564   @param[in]  ceed Ceed object where the CeedOperator will be created
565   @param[out] op   Address of the variable where the newly created Composite CeedOperator will be stored
566 
567   @return An error code: 0 - success, otherwise - failure
568 
569   @ref User
570  */
571 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
572   if (!ceed->CompositeOperatorCreate) {
573     Ceed delegate;
574     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Operator"));
575 
576     if (delegate) {
577       CeedCall(CeedCompositeOperatorCreate(delegate, op));
578       return CEED_ERROR_SUCCESS;
579     }
580   }
581 
582   CeedCall(CeedCalloc(1, op));
583   (*op)->ceed = ceed;
584   CeedCall(CeedReference(ceed));
585   (*op)->ref_count    = 1;
586   (*op)->is_composite = true;
587   CeedCall(CeedCalloc(CEED_COMPOSITE_MAX, &(*op)->sub_operators));
588   (*op)->input_size  = -1;
589   (*op)->output_size = -1;
590 
591   if (ceed->CompositeOperatorCreate) {
592     CeedCall(ceed->CompositeOperatorCreate(*op));
593   }
594   return CEED_ERROR_SUCCESS;
595 }
596 
597 /**
598   @brief Copy the pointer to a CeedOperator.
599 
600   Both pointers should be destroyed with `CeedOperatorDestroy()`.
601 
602   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.
603         This CeedOperator will be destroyed if `op_copy` is the only reference to this CeedOperator.
604 
605   @param[in]  op         CeedOperator to copy reference to
606   @param[in,out] op_copy Variable to store copied reference
607 
608   @return An error code: 0 - success, otherwise - failure
609 
610   @ref User
611 **/
612 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
613   CeedCall(CeedOperatorReference(op));
614   CeedCall(CeedOperatorDestroy(op_copy));
615   *op_copy = op;
616   return CEED_ERROR_SUCCESS;
617 }
618 
619 /**
620   @brief Provide a field to a CeedOperator for use by its CeedQFunction.
621 
622   This function is used to specify both active and passive fields to a CeedOperator.
623   For passive fields, a vector @arg v must be provided.
624   Passive fields can inputs or outputs (updated in-place when operator is applied).
625 
626   Active fields must be specified using this function, but their data (in a CeedVector) is passed in CeedOperatorApply().
627   There can be at most one active input CeedVector and at most one active output CeedVector passed to CeedOperatorApply().
628 
629   @param[in,out] op         CeedOperator on which to provide the field
630   @param[in]     field_name Name of the field (to be matched with the name used by CeedQFunction)
631   @param[in]     r          CeedElemRestriction
632   @param[in]     b          CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED if collocated with quadrature points
633   @param[in]     v          CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE if field is active or @ref CEED_VECTOR_NONE
634                               if using @ref CEED_EVAL_WEIGHT in the QFunction
635 
636   @return An error code: 0 - success, otherwise - failure
637 
638   @ref User
639 **/
640 int CeedOperatorSetField(CeedOperator op, const char *field_name, CeedElemRestriction r, CeedBasis b, CeedVector v) {
641   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot add field to composite operator.");
642   CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
643   CeedCheck(r, op->ceed, CEED_ERROR_INCOMPATIBLE, "ElemRestriction r for field \"%s\" must be non-NULL.", field_name);
644   CeedCheck(b, op->ceed, CEED_ERROR_INCOMPATIBLE, "Basis b for field \"%s\" must be non-NULL.", field_name);
645   CeedCheck(v, op->ceed, CEED_ERROR_INCOMPATIBLE, "Vector v for field \"%s\" must be non-NULL.", field_name);
646 
647   CeedInt num_elem = 0;
648   CeedCall(CeedElemRestrictionGetNumElements(r, &num_elem));
649   CeedCheck(r == CEED_ELEMRESTRICTION_NONE || !op->has_restriction || op->num_elem == num_elem, op->ceed, CEED_ERROR_DIMENSION,
650             "ElemRestriction with %" CeedInt_FMT " elements incompatible with prior %" CeedInt_FMT " elements", num_elem, op->num_elem);
651 
652   CeedInt num_qpts = 0;
653   CeedCall(CeedBasisGetNumQuadraturePoints(b, &num_qpts));
654   CeedCheck(b == CEED_BASIS_COLLOCATED || !op->num_qpts || op->num_qpts == num_qpts, op->ceed, CEED_ERROR_DIMENSION,
655             "Basis with %" CeedInt_FMT " quadrature points incompatible with prior %" CeedInt_FMT " points", num_qpts, op->num_qpts);
656   CeedQFunctionField qf_field;
657   CeedOperatorField *op_field;
658   bool               is_input = true;
659   for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
660     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
661       qf_field = op->qf->input_fields[i];
662       op_field = &op->input_fields[i];
663       goto found;
664     }
665   }
666   is_input = false;
667   for (CeedInt i = 0; i < op->qf->num_output_fields; i++) {
668     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
669       qf_field = op->qf->output_fields[i];
670       op_field = &op->output_fields[i];
671       goto found;
672     }
673   }
674   // LCOV_EXCL_START
675   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE, "QFunction has no knowledge of field '%s'", field_name);
676   // LCOV_EXCL_STOP
677 found:
678   CeedCall(CeedOperatorCheckField(op->ceed, qf_field, r, b));
679   CeedCall(CeedCalloc(1, op_field));
680 
681   if (v == CEED_VECTOR_ACTIVE) {
682     CeedSize l_size;
683     CeedCall(CeedElemRestrictionGetLVectorSize(r, &l_size));
684     if (is_input) {
685       if (op->input_size == -1) op->input_size = l_size;
686       CeedCheck(l_size == op->input_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size,
687                 op->input_size);
688     } else {
689       if (op->output_size == -1) op->output_size = l_size;
690       CeedCheck(l_size == op->output_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size,
691                 op->output_size);
692     }
693   }
694 
695   CeedCall(CeedVectorReferenceCopy(v, &(*op_field)->vec));
696   CeedCall(CeedElemRestrictionReferenceCopy(r, &(*op_field)->elem_rstr));
697   if (r != CEED_ELEMRESTRICTION_NONE) {
698     op->num_elem        = num_elem;
699     op->has_restriction = true;  // Restriction set, but num_elem may be 0
700   }
701   CeedCall(CeedBasisReferenceCopy(b, &(*op_field)->basis));
702   if (!op->num_qpts && b != CEED_BASIS_COLLOCATED) CeedCall(CeedOperatorSetNumQuadraturePoints(op, num_qpts));
703 
704   op->num_fields += 1;
705   CeedCall(CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name));
706   return CEED_ERROR_SUCCESS;
707 }
708 
709 /**
710   @brief Get the CeedOperatorFields of a CeedOperator
711 
712   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
713 
714   @param[in]  op                CeedOperator
715   @param[out] num_input_fields  Variable to store number of input fields
716   @param[out] input_fields      Variable to store input_fields
717   @param[out] num_output_fields Variable to store number of output fields
718   @param[out] output_fields     Variable to store output_fields
719 
720   @return An error code: 0 - success, otherwise - failure
721 
722   @ref Advanced
723 **/
724 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields, CeedOperatorField **input_fields, CeedInt *num_output_fields,
725                           CeedOperatorField **output_fields) {
726   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
727   CeedCall(CeedOperatorCheckReady(op));
728 
729   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
730   if (input_fields) *input_fields = op->input_fields;
731   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
732   if (output_fields) *output_fields = op->output_fields;
733   return CEED_ERROR_SUCCESS;
734 }
735 
736 /**
737   @brief Get a CeedOperatorField of an CeedOperator from its name
738 
739   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
740 
741   @param[in]  op         CeedOperator
742   @param[in]  field_name Name of desired CeedOperatorField
743   @param[out] op_field   CeedOperatorField corresponding to the name
744 
745   @return An error code: 0 - success, otherwise - failure
746 
747   @ref Advanced
748 **/
749 int CeedOperatorGetFieldByName(CeedOperator op, const char *field_name, CeedOperatorField *op_field) {
750   CeedInt            num_input_fields, num_output_fields;
751   CeedOperatorField *input_fields, *output_fields;
752   char              *name;
753   CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields));
754 
755   for (CeedInt i = 0; i < num_input_fields; i++) {
756     CeedCall(CeedOperatorFieldGetName(input_fields[i], &name));
757     if (!strcmp(name, field_name)) {
758       *op_field = input_fields[i];
759       return CEED_ERROR_SUCCESS;
760     }
761   }
762 
763   for (CeedInt i = 0; i < num_output_fields; i++) {
764     CeedCall(CeedOperatorFieldGetName(output_fields[i], &name));
765     if (!strcmp(name, field_name)) {
766       *op_field = output_fields[i];
767       return CEED_ERROR_SUCCESS;
768     }
769   }
770 
771   bool has_name = op->name;
772   // LCOV_EXCL_START
773   return CeedError(op->ceed, CEED_ERROR_MINOR, "The field \"%s\" not found in CeedOperator%s%s%s.\n", field_name, has_name ? " \"" : "",
774                    has_name ? op->name : "", has_name ? "\"" : "");
775   // LCOV_EXCL_STOP
776 }
777 
778 /**
779   @brief Get the name of a CeedOperatorField
780 
781   @param[in]  op_field    CeedOperatorField
782   @param[out] field_name  Variable to store the field name
783 
784   @return An error code: 0 - success, otherwise - failure
785 
786   @ref Advanced
787 **/
788 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
789   *field_name = (char *)op_field->field_name;
790   return CEED_ERROR_SUCCESS;
791 }
792 
793 /**
794   @brief Get the CeedElemRestriction of a CeedOperatorField
795 
796   @param[in]  op_field CeedOperatorField
797   @param[out] rstr     Variable to store CeedElemRestriction
798 
799   @return An error code: 0 - success, otherwise - failure
800 
801   @ref Advanced
802 **/
803 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field, CeedElemRestriction *rstr) {
804   *rstr = op_field->elem_rstr;
805   return CEED_ERROR_SUCCESS;
806 }
807 
808 /**
809   @brief Get the CeedBasis of a CeedOperatorField
810 
811   @param[in]  op_field CeedOperatorField
812   @param[out] basis    Variable to store CeedBasis
813 
814   @return An error code: 0 - success, otherwise - failure
815 
816   @ref Advanced
817 **/
818 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
819   *basis = op_field->basis;
820   return CEED_ERROR_SUCCESS;
821 }
822 
823 /**
824   @brief Get the CeedVector of a CeedOperatorField
825 
826   @param[in]  op_field CeedOperatorField
827   @param[out] vec      Variable to store CeedVector
828 
829   @return An error code: 0 - success, otherwise - failure
830 
831   @ref Advanced
832 **/
833 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
834   *vec = op_field->vec;
835   return CEED_ERROR_SUCCESS;
836 }
837 
838 /**
839   @brief Add a sub-operator to a composite CeedOperator
840 
841   @param[in,out] composite_op Composite CeedOperator
842   @param[in]     sub_op       Sub-operator CeedOperator
843 
844   @return An error code: 0 - success, otherwise - failure
845 
846   @ref User
847  */
848 int CeedCompositeOperatorAddSub(CeedOperator composite_op, CeedOperator sub_op) {
849   CeedCheck(composite_op->is_composite, composite_op->ceed, CEED_ERROR_MINOR, "CeedOperator is not a composite operator");
850   CeedCheck(composite_op->num_suboperators < CEED_COMPOSITE_MAX, composite_op->ceed, CEED_ERROR_UNSUPPORTED, "Cannot add additional sub-operators");
851   CeedCheck(!composite_op->is_immutable, composite_op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
852 
853   {
854     CeedSize input_size, output_size;
855     CeedCall(CeedOperatorGetActiveVectorLengths(sub_op, &input_size, &output_size));
856     if (composite_op->input_size == -1) composite_op->input_size = input_size;
857     if (composite_op->output_size == -1) composite_op->output_size = output_size;
858     // Note, a size of -1 means no active vector restriction set, so no incompatibility
859     CeedCheck((input_size == -1 || input_size == composite_op->input_size) && (output_size == -1 || output_size == composite_op->output_size),
860               composite_op->ceed, CEED_ERROR_MAJOR,
861               "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of "
862               "shape (%td, %td)",
863               composite_op->input_size, composite_op->output_size, input_size, output_size);
864   }
865 
866   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
867   CeedCall(CeedOperatorReference(sub_op));
868   composite_op->num_suboperators++;
869   return CEED_ERROR_SUCCESS;
870 }
871 
872 /**
873   @brief Get the number of sub_operators associated with a CeedOperator
874 
875   @param[in]  op               CeedOperator
876   @param[out] num_suboperators Variable to store number of sub_operators
877 
878   @return An error code: 0 - success, otherwise - failure
879 
880   @ref Backend
881 **/
882 int CeedCompositeOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
883   CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator");
884   *num_suboperators = op->num_suboperators;
885   return CEED_ERROR_SUCCESS;
886 }
887 
888 /**
889   @brief Get the list of sub_operators associated with a CeedOperator
890 
891   @param op                  CeedOperator
892   @param[out] sub_operators  Variable to store list of sub_operators
893 
894   @return An error code: 0 - success, otherwise - failure
895 
896   @ref Backend
897 **/
898 int CeedCompositeOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
899   CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator");
900   *sub_operators = op->sub_operators;
901   return CEED_ERROR_SUCCESS;
902 }
903 
904 /**
905   @brief Check if a CeedOperator is ready to be used.
906 
907   @param[in] op CeedOperator to check
908 
909   @return An error code: 0 - success, otherwise - failure
910 
911   @ref User
912 **/
913 int CeedOperatorCheckReady(CeedOperator op) {
914   Ceed ceed;
915   CeedCall(CeedOperatorGetCeed(op, &ceed));
916 
917   if (op->is_interface_setup) return CEED_ERROR_SUCCESS;
918 
919   CeedQFunction qf = op->qf;
920   if (op->is_composite) {
921     if (!op->num_suboperators) {
922       // Empty operator setup
923       op->input_size  = 0;
924       op->output_size = 0;
925     } else {
926       for (CeedInt i = 0; i < op->num_suboperators; i++) {
927         CeedCall(CeedOperatorCheckReady(op->sub_operators[i]));
928       }
929       // Sub-operators could be modified after adding to composite operator
930       // Need to verify no lvec incompatibility from any changes
931       CeedSize input_size, output_size;
932       CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
933     }
934   } else {
935     CeedCheck(op->num_fields > 0, ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
936     CeedCheck(op->num_fields == qf->num_input_fields + qf->num_output_fields, ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
937     CeedCheck(op->has_restriction, ceed, CEED_ERROR_INCOMPLETE, "At least one restriction required");
938     CeedCheck(op->num_qpts > 0, ceed, CEED_ERROR_INCOMPLETE,
939               "At least one non-collocated basis is required or the number of quadrature points must be set");
940   }
941 
942   // Flag as immutable and ready
943   op->is_interface_setup = true;
944   if (op->qf && op->qf != CEED_QFUNCTION_NONE) op->qf->is_immutable = true;
945   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE) op->dqf->is_immutable = true;
946   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE) op->dqfT->is_immutable = true;
947   return CEED_ERROR_SUCCESS;
948 }
949 
950 /**
951   @brief Get vector lengths for the active input and/or output vectors of a CeedOperator.
952 
953   Note: Lengths of -1 indicate that the CeedOperator does not have an active input and/or output.
954 
955   @param[in]  op          CeedOperator
956   @param[out] input_size  Variable to store active input vector length, or NULL
957   @param[out] output_size Variable to store active output vector length, or NULL
958 
959   @return An error code: 0 - success, otherwise - failure
960 
961   @ref User
962 **/
963 int CeedOperatorGetActiveVectorLengths(CeedOperator op, CeedSize *input_size, CeedSize *output_size) {
964   bool is_composite;
965 
966   if (input_size) *input_size = op->input_size;
967   if (output_size) *output_size = op->output_size;
968 
969   CeedCall(CeedOperatorIsComposite(op, &is_composite));
970   if (is_composite && (op->input_size == -1 || op->output_size == -1)) {
971     for (CeedInt i = 0; i < op->num_suboperators; i++) {
972       CeedSize sub_input_size, sub_output_size;
973       CeedCall(CeedOperatorGetActiveVectorLengths(op->sub_operators[i], &sub_input_size, &sub_output_size));
974       if (op->input_size == -1) op->input_size = sub_input_size;
975       if (op->output_size == -1) op->output_size = sub_output_size;
976       // Note, a size of -1 means no active vector restriction set, so no incompatibility
977       CeedCheck((sub_input_size == -1 || sub_input_size == op->input_size) && (sub_output_size == -1 || sub_output_size == op->output_size), op->ceed,
978                 CEED_ERROR_MAJOR,
979                 "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of "
980                 "shape (%td, %td)",
981                 op->input_size, op->output_size, input_size, output_size);
982     }
983   }
984 
985   return CEED_ERROR_SUCCESS;
986 }
987 
988 /**
989   @brief Set reuse of CeedQFunction data in CeedOperatorLinearAssemble* functions.
990 
991   When `reuse_assembly_data = false` (default), the CeedQFunction associated with this CeedOperator is re-assembled every time a
992 `CeedOperatorLinearAssemble*` function is called.
993   When `reuse_assembly_data = true`, the CeedQFunction associated with this CeedOperator is reused between calls to
994 `CeedOperatorSetQFunctionAssemblyDataUpdated`.
995 
996   @param[in] op                  CeedOperator
997   @param[in] reuse_assembly_data Boolean flag setting assembly data reuse
998 
999   @return An error code: 0 - success, otherwise - failure
1000 
1001   @ref Advanced
1002 **/
1003 int CeedOperatorSetQFunctionAssemblyReuse(CeedOperator op, bool reuse_assembly_data) {
1004   bool is_composite;
1005 
1006   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1007   if (is_composite) {
1008     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1009       CeedCall(CeedOperatorSetQFunctionAssemblyReuse(op->sub_operators[i], reuse_assembly_data));
1010     }
1011   } else {
1012     CeedCall(CeedQFunctionAssemblyDataSetReuse(op->qf_assembled, reuse_assembly_data));
1013   }
1014 
1015   return CEED_ERROR_SUCCESS;
1016 }
1017 
1018 /**
1019   @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly.
1020 
1021   @param[in] op                CeedOperator
1022   @param[in] needs_data_update Boolean flag setting assembly data reuse
1023 
1024   @return An error code: 0 - success, otherwise - failure
1025 
1026   @ref Advanced
1027 **/
1028 int CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(CeedOperator op, bool needs_data_update) {
1029   bool is_composite;
1030 
1031   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1032   if (is_composite) {
1033     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1034       CeedCall(CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(op->sub_operators[i], needs_data_update));
1035     }
1036   } else {
1037     CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, needs_data_update));
1038   }
1039 
1040   return CEED_ERROR_SUCCESS;
1041 }
1042 
1043 /**
1044   @brief Set the number of quadrature points associated with a CeedOperator.
1045 
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     CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields));
1193     CeedInt num_elem = 0;
1194     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
1195 
1196     // Input FLOPs
1197     for (CeedInt i = 0; i < num_input_fields; i++) {
1198       if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1199         CeedSize restr_flops, basis_flops;
1200 
1201         CeedCall(CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_rstr, CEED_NOTRANSPOSE, &restr_flops));
1202         *flops += restr_flops;
1203         CeedCall(CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE, op->qf->input_fields[i]->eval_mode, &basis_flops));
1204         *flops += basis_flops * num_elem;
1205       }
1206     }
1207     // QF FLOPs
1208     CeedInt  num_qpts;
1209     CeedSize qf_flops;
1210     CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts));
1211     CeedCall(CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops));
1212     *flops += num_elem * num_qpts * qf_flops;
1213     // Output FLOPs
1214     for (CeedInt i = 0; i < num_output_fields; i++) {
1215       if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1216         CeedSize restr_flops, basis_flops;
1217 
1218         CeedCall(CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_rstr, CEED_TRANSPOSE, &restr_flops));
1219         *flops += restr_flops;
1220         CeedCall(CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE, op->qf->output_fields[i]->eval_mode, &basis_flops));
1221         *flops += basis_flops * num_elem;
1222       }
1223     }
1224   }
1225 
1226   return CEED_ERROR_SUCCESS;
1227 }
1228 
1229 /**
1230   @brief Get CeedQFunction global context for a CeedOperator.
1231 
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 CeedQFunctionContext.
1235         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 
1354   For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`.
1355 
1356   @param[in,out] op          CeedOperator
1357   @param[in]     field_label Label of field to set
1358   @param[in]     values      Values to set
1359 
1360   @return An error code: 0 - success, otherwise - failure
1361 
1362   @ref User
1363 **/
1364 int CeedOperatorSetContextDouble(CeedOperator op, CeedContextFieldLabel field_label, double *values) {
1365   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values);
1366 }
1367 
1368 /**
1369   @brief Get QFunctionContext field holding double precision values, read-only.
1370 
1371   For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`.
1372 
1373   @param[in]  op          CeedOperator
1374   @param[in]  field_label Label of field to get
1375   @param[out] num_values  Number of values in the field label
1376   @param[out] values      Pointer to context values
1377 
1378   @return An error code: 0 - success, otherwise - failure
1379 
1380   @ref User
1381 **/
1382 int CeedOperatorGetContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const double **values) {
1383   return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, num_values, values);
1384 }
1385 
1386 /**
1387   @brief Restore QFunctionContext field holding double precision values, read-only.
1388 
1389   @param[in]  op          CeedOperator
1390   @param[in]  field_label Label of field to restore
1391   @param[out] values      Pointer to context values
1392 
1393   @return An error code: 0 - success, otherwise - failure
1394 
1395   @ref User
1396 **/
1397 int CeedOperatorRestoreContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, const double **values) {
1398   return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values);
1399 }
1400 
1401 /**
1402   @brief Set QFunctionContext field holding int32 values.
1403 
1404   For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`.
1405 
1406   @param[in,out] op          CeedOperator
1407   @param[in]     field_label Label of field to set
1408   @param[in]     values      Values to set
1409 
1410   @return An error code: 0 - success, otherwise - failure
1411 
1412   @ref User
1413 **/
1414 int CeedOperatorSetContextInt32(CeedOperator op, CeedContextFieldLabel field_label, int *values) {
1415   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32, values);
1416 }
1417 
1418 /**
1419   @brief Get QFunctionContext field holding int32 values, read-only.
1420 
1421   For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`.
1422 
1423   @param[in]  op          CeedOperator
1424   @param[in]  field_label Label of field to get
1425   @param[out] num_values  Number of int32 values in `values`
1426   @param[out] values      Pointer to context values
1427 
1428   @return An error code: 0 - success, otherwise - failure
1429 
1430   @ref User
1431 **/
1432 int CeedOperatorGetContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const int **values) {
1433   return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, num_values, values);
1434 }
1435 
1436 /**
1437   @brief Restore QFunctionContext field holding int32 values, read-only.
1438 
1439   @param[in]  op          CeedOperator
1440   @param[in]  field_label Label of field to get
1441   @param[out] values      Pointer to context values
1442 
1443   @return An error code: 0 - success, otherwise - failure
1444 
1445   @ref User
1446 **/
1447 int CeedOperatorRestoreContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, const int **values) {
1448   return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, values);
1449 }
1450 
1451 /**
1452   @brief Apply CeedOperator to a vector
1453 
1454   This computes the action of the operator on the specified (active) input, yielding its (active) output.
1455   All inputs and outputs must be specified using CeedOperatorSetField().
1456 
1457   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1458 
1459   @param[in]  op      CeedOperator to apply
1460   @param[in]  in      CeedVector containing input state or @ref CEED_VECTOR_NONE if there are no active inputs
1461   @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
1462 active outputs
1463   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1464 
1465   @return An error code: 0 - success, otherwise - failure
1466 
1467   @ref User
1468 **/
1469 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) {
1470   CeedCall(CeedOperatorCheckReady(op));
1471 
1472   if (op->num_elem) {
1473     // Standard Operator
1474     if (op->Apply) {
1475       CeedCall(op->Apply(op, in, out, request));
1476     } else {
1477       // Zero all output vectors
1478       CeedQFunction qf = op->qf;
1479       for (CeedInt i = 0; i < qf->num_output_fields; i++) {
1480         CeedVector vec = op->output_fields[i]->vec;
1481         if (vec == CEED_VECTOR_ACTIVE) vec = out;
1482         if (vec != CEED_VECTOR_NONE) {
1483           CeedCall(CeedVectorSetValue(vec, 0.0));
1484         }
1485       }
1486       // Apply
1487       CeedCall(op->ApplyAdd(op, in, out, request));
1488     }
1489   } else if (op->is_composite) {
1490     // Composite Operator
1491     if (op->ApplyComposite) {
1492       CeedCall(op->ApplyComposite(op, in, out, request));
1493     } else {
1494       CeedInt num_suboperators;
1495       CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1496       CeedOperator *sub_operators;
1497       CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1498 
1499       // Zero all output vectors
1500       if (out != CEED_VECTOR_NONE) {
1501         CeedCall(CeedVectorSetValue(out, 0.0));
1502       }
1503       for (CeedInt i = 0; i < num_suboperators; i++) {
1504         for (CeedInt j = 0; j < sub_operators[i]->qf->num_output_fields; j++) {
1505           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1506           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1507             CeedCall(CeedVectorSetValue(vec, 0.0));
1508           }
1509         }
1510       }
1511       // Apply
1512       for (CeedInt i = 0; i < op->num_suboperators; i++) {
1513         CeedCall(CeedOperatorApplyAdd(op->sub_operators[i], in, out, request));
1514       }
1515     }
1516   }
1517   return CEED_ERROR_SUCCESS;
1518 }
1519 
1520 /**
1521   @brief Apply CeedOperator to a vector and add result to output vector
1522 
1523   This computes the action of the operator on the specified (active) input, yielding its (active) output.
1524   All inputs and outputs must be specified using CeedOperatorSetField().
1525 
1526   @param[in]  op      CeedOperator to apply
1527   @param[in]  in      CeedVector containing input state or NULL if there are no active inputs
1528   @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
1529   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1530 
1531   @return An error code: 0 - success, otherwise - failure
1532 
1533   @ref User
1534 **/
1535 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) {
1536   CeedCall(CeedOperatorCheckReady(op));
1537 
1538   if (op->num_elem) {
1539     // Standard Operator
1540     CeedCall(op->ApplyAdd(op, in, out, request));
1541   } else if (op->is_composite) {
1542     // Composite Operator
1543     if (op->ApplyAddComposite) {
1544       CeedCall(op->ApplyAddComposite(op, in, out, request));
1545     } else {
1546       CeedInt num_suboperators;
1547       CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1548       CeedOperator *sub_operators;
1549       CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1550 
1551       for (CeedInt i = 0; i < num_suboperators; i++) {
1552         CeedCall(CeedOperatorApplyAdd(sub_operators[i], in, out, request));
1553       }
1554     }
1555   }
1556   return CEED_ERROR_SUCCESS;
1557 }
1558 
1559 /**
1560   @brief Destroy a CeedOperator
1561 
1562   @param[in,out] op CeedOperator to destroy
1563 
1564   @return An error code: 0 - success, otherwise - failure
1565 
1566   @ref User
1567 **/
1568 int CeedOperatorDestroy(CeedOperator *op) {
1569   if (!*op || --(*op)->ref_count > 0) {
1570     *op = NULL;
1571     return CEED_ERROR_SUCCESS;
1572   }
1573   if ((*op)->Destroy) CeedCall((*op)->Destroy(*op));
1574   CeedCall(CeedDestroy(&(*op)->ceed));
1575   // Free fields
1576   for (CeedInt i = 0; i < (*op)->num_fields; i++) {
1577     if ((*op)->input_fields[i]) {
1578       if ((*op)->input_fields[i]->elem_rstr != CEED_ELEMRESTRICTION_NONE) {
1579         CeedCall(CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_rstr));
1580       }
1581       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1582         CeedCall(CeedBasisDestroy(&(*op)->input_fields[i]->basis));
1583       }
1584       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->input_fields[i]->vec != CEED_VECTOR_NONE) {
1585         CeedCall(CeedVectorDestroy(&(*op)->input_fields[i]->vec));
1586       }
1587       CeedCall(CeedFree(&(*op)->input_fields[i]->field_name));
1588       CeedCall(CeedFree(&(*op)->input_fields[i]));
1589     }
1590   }
1591   for (CeedInt i = 0; i < (*op)->num_fields; i++) {
1592     if ((*op)->output_fields[i]) {
1593       CeedCall(CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_rstr));
1594       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1595         CeedCall(CeedBasisDestroy(&(*op)->output_fields[i]->basis));
1596       }
1597       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->output_fields[i]->vec != CEED_VECTOR_NONE) {
1598         CeedCall(CeedVectorDestroy(&(*op)->output_fields[i]->vec));
1599       }
1600       CeedCall(CeedFree(&(*op)->output_fields[i]->field_name));
1601       CeedCall(CeedFree(&(*op)->output_fields[i]));
1602     }
1603   }
1604   // Destroy sub_operators
1605   for (CeedInt i = 0; i < (*op)->num_suboperators; i++) {
1606     if ((*op)->sub_operators[i]) {
1607       CeedCall(CeedOperatorDestroy(&(*op)->sub_operators[i]));
1608     }
1609   }
1610   CeedCall(CeedQFunctionDestroy(&(*op)->qf));
1611   CeedCall(CeedQFunctionDestroy(&(*op)->dqf));
1612   CeedCall(CeedQFunctionDestroy(&(*op)->dqfT));
1613   // Destroy any composite labels
1614   if ((*op)->is_composite) {
1615     for (CeedInt i = 0; i < (*op)->num_context_labels; i++) {
1616       CeedCall(CeedFree(&(*op)->context_labels[i]->sub_labels));
1617       CeedCall(CeedFree(&(*op)->context_labels[i]));
1618     }
1619   }
1620   CeedCall(CeedFree(&(*op)->context_labels));
1621 
1622   // Destroy fallback
1623   CeedCall(CeedOperatorDestroy(&(*op)->op_fallback));
1624 
1625   // Destroy assembly data
1626   CeedCall(CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled));
1627   CeedCall(CeedOperatorAssemblyDataDestroy(&(*op)->op_assembled));
1628 
1629   CeedCall(CeedFree(&(*op)->input_fields));
1630   CeedCall(CeedFree(&(*op)->output_fields));
1631   CeedCall(CeedFree(&(*op)->sub_operators));
1632   CeedCall(CeedFree(&(*op)->name));
1633   CeedCall(CeedFree(op));
1634   return CEED_ERROR_SUCCESS;
1635 }
1636 
1637 /// @}
1638