xref: /libCEED/interface/ceed-operator.c (revision d9267b7650b1f58e68a23a57668f7a3f0af9c36a)
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 
539   CeedCall(CeedCalloc(1, op));
540   CeedCall(CeedReferenceCopy(ceed, &(*op)->ceed));
541   (*op)->ref_count   = 1;
542   (*op)->input_size  = -1;
543   (*op)->output_size = -1;
544   CeedCall(CeedQFunctionReferenceCopy(qf, &(*op)->qf));
545   if (dqf && dqf != CEED_QFUNCTION_NONE) CeedCall(CeedQFunctionReferenceCopy(dqf, &(*op)->dqf));
546   if (dqfT && dqfT != CEED_QFUNCTION_NONE) CeedCall(CeedQFunctionReferenceCopy(dqfT, &(*op)->dqfT));
547   CeedCall(CeedQFunctionAssemblyDataCreate(ceed, &(*op)->qf_assembled));
548   CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->input_fields));
549   CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->output_fields));
550   CeedCall(ceed->OperatorCreate(*op));
551   return CEED_ERROR_SUCCESS;
552 }
553 
554 /**
555   @brief Create an operator that composes the action of several operators
556 
557   @param[in]  ceed Ceed object where the CeedOperator will be created
558   @param[out] op   Address of the variable where the newly created Composite CeedOperator will be stored
559 
560   @return An error code: 0 - success, otherwise - failure
561 
562   @ref User
563  */
564 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
565   if (!ceed->CompositeOperatorCreate) {
566     Ceed delegate;
567     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Operator"));
568 
569     if (delegate) {
570       CeedCall(CeedCompositeOperatorCreate(delegate, op));
571       return CEED_ERROR_SUCCESS;
572     }
573   }
574 
575   CeedCall(CeedCalloc(1, op));
576   CeedCall(CeedReferenceCopy(ceed, &(*op)->ceed));
577   (*op)->ref_count    = 1;
578   (*op)->is_composite = true;
579   CeedCall(CeedCalloc(CEED_COMPOSITE_MAX, &(*op)->sub_operators));
580   (*op)->input_size  = -1;
581   (*op)->output_size = -1;
582 
583   if (ceed->CompositeOperatorCreate) CeedCall(ceed->CompositeOperatorCreate(*op));
584   return CEED_ERROR_SUCCESS;
585 }
586 
587 /**
588   @brief Copy the pointer to a CeedOperator.
589 
590   Both pointers should be destroyed with `CeedOperatorDestroy()`.
591 
592   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.
593         This CeedOperator will be destroyed if `op_copy` is the only reference to this CeedOperator.
594 
595   @param[in]  op         CeedOperator to copy reference to
596   @param[in,out] op_copy Variable to store copied reference
597 
598   @return An error code: 0 - success, otherwise - failure
599 
600   @ref User
601 **/
602 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
603   CeedCall(CeedOperatorReference(op));
604   CeedCall(CeedOperatorDestroy(op_copy));
605   *op_copy = op;
606   return CEED_ERROR_SUCCESS;
607 }
608 
609 /**
610   @brief Provide a field to a CeedOperator for use by its CeedQFunction.
611 
612   This function is used to specify both active and passive fields to a CeedOperator.
613   For passive fields, a vector @arg v must be provided.
614   Passive fields can inputs or outputs (updated in-place when operator is applied).
615 
616   Active fields must be specified using this function, but their data (in a CeedVector) is passed in CeedOperatorApply().
617   There can be at most one active input CeedVector and at most one active output CeedVector passed to CeedOperatorApply().
618 
619   @param[in,out] op         CeedOperator on which to provide the field
620   @param[in]     field_name Name of the field (to be matched with the name used by CeedQFunction)
621   @param[in]     r          CeedElemRestriction
622   @param[in]     b          CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED if collocated with quadrature points
623   @param[in]     v          CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE if field is active or @ref CEED_VECTOR_NONE
624                               if using @ref CEED_EVAL_WEIGHT in the QFunction
625 
626   @return An error code: 0 - success, otherwise - failure
627 
628   @ref User
629 **/
630 int CeedOperatorSetField(CeedOperator op, const char *field_name, CeedElemRestriction r, CeedBasis b, CeedVector v) {
631   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot add field to composite operator.");
632   CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
633   CeedCheck(r, op->ceed, CEED_ERROR_INCOMPATIBLE, "ElemRestriction r for field \"%s\" must be non-NULL.", field_name);
634   CeedCheck(b, op->ceed, CEED_ERROR_INCOMPATIBLE, "Basis b for field \"%s\" must be non-NULL.", field_name);
635   CeedCheck(v, op->ceed, CEED_ERROR_INCOMPATIBLE, "Vector v for field \"%s\" must be non-NULL.", field_name);
636 
637   CeedInt num_elem = 0;
638   CeedCall(CeedElemRestrictionGetNumElements(r, &num_elem));
639   CeedCheck(r == CEED_ELEMRESTRICTION_NONE || !op->has_restriction || op->num_elem == num_elem, op->ceed, CEED_ERROR_DIMENSION,
640             "ElemRestriction with %" CeedInt_FMT " elements incompatible with prior %" CeedInt_FMT " elements", num_elem, op->num_elem);
641 
642   CeedInt num_qpts = 0;
643   if (b == CEED_BASIS_COLLOCATED) CeedCall(CeedElemRestrictionGetElementSize(r, &num_qpts));
644   else CeedCall(CeedBasisGetNumQuadraturePoints(b, &num_qpts));
645   CeedCheck(!op->num_qpts || op->num_qpts == num_qpts, op->ceed, CEED_ERROR_DIMENSION,
646             "Basis with %" CeedInt_FMT " quadrature points incompatible with prior %" CeedInt_FMT " points", num_qpts, op->num_qpts);
647   CeedQFunctionField qf_field;
648   CeedOperatorField *op_field;
649   bool               is_input = true;
650   for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
651     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
652       qf_field = op->qf->input_fields[i];
653       op_field = &op->input_fields[i];
654       goto found;
655     }
656   }
657   is_input = false;
658   for (CeedInt i = 0; i < op->qf->num_output_fields; i++) {
659     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
660       qf_field = op->qf->output_fields[i];
661       op_field = &op->output_fields[i];
662       goto found;
663     }
664   }
665   // LCOV_EXCL_START
666   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE, "QFunction has no knowledge of field '%s'", field_name);
667   // LCOV_EXCL_STOP
668 found:
669   CeedCall(CeedOperatorCheckField(op->ceed, qf_field, r, b));
670   CeedCall(CeedCalloc(1, op_field));
671 
672   if (v == CEED_VECTOR_ACTIVE) {
673     CeedSize l_size;
674     CeedCall(CeedElemRestrictionGetLVectorSize(r, &l_size));
675     if (is_input) {
676       if (op->input_size == -1) op->input_size = l_size;
677       CeedCheck(l_size == op->input_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size,
678                 op->input_size);
679     } else {
680       if (op->output_size == -1) op->output_size = l_size;
681       CeedCheck(l_size == op->output_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size,
682                 op->output_size);
683     }
684   }
685 
686   CeedCall(CeedVectorReferenceCopy(v, &(*op_field)->vec));
687   CeedCall(CeedElemRestrictionReferenceCopy(r, &(*op_field)->elem_rstr));
688   if (r != CEED_ELEMRESTRICTION_NONE) {
689     op->num_elem        = num_elem;
690     op->has_restriction = true;  // Restriction set, but num_elem may be 0
691   }
692   CeedCall(CeedBasisReferenceCopy(b, &(*op_field)->basis));
693   if (!op->num_qpts && b != CEED_BASIS_COLLOCATED) CeedCall(CeedOperatorSetNumQuadraturePoints(op, num_qpts));
694 
695   op->num_fields += 1;
696   CeedCall(CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name));
697   return CEED_ERROR_SUCCESS;
698 }
699 
700 /**
701   @brief Get the CeedOperatorFields of a CeedOperator
702 
703   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
704 
705   @param[in]  op                CeedOperator
706   @param[out] num_input_fields  Variable to store number of input fields
707   @param[out] input_fields      Variable to store input_fields
708   @param[out] num_output_fields Variable to store number of output fields
709   @param[out] output_fields     Variable to store output_fields
710 
711   @return An error code: 0 - success, otherwise - failure
712 
713   @ref Advanced
714 **/
715 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields, CeedOperatorField **input_fields, CeedInt *num_output_fields,
716                           CeedOperatorField **output_fields) {
717   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
718   CeedCall(CeedOperatorCheckReady(op));
719 
720   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
721   if (input_fields) *input_fields = op->input_fields;
722   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
723   if (output_fields) *output_fields = op->output_fields;
724   return CEED_ERROR_SUCCESS;
725 }
726 
727 /**
728   @brief Get a CeedOperatorField of an CeedOperator from its name
729 
730   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
731 
732   @param[in]  op         CeedOperator
733   @param[in]  field_name Name of desired CeedOperatorField
734   @param[out] op_field   CeedOperatorField corresponding to the name
735 
736   @return An error code: 0 - success, otherwise - failure
737 
738   @ref Advanced
739 **/
740 int CeedOperatorGetFieldByName(CeedOperator op, const char *field_name, CeedOperatorField *op_field) {
741   CeedInt            num_input_fields, num_output_fields;
742   CeedOperatorField *input_fields, *output_fields;
743   char              *name;
744   CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields));
745 
746   for (CeedInt i = 0; i < num_input_fields; i++) {
747     CeedCall(CeedOperatorFieldGetName(input_fields[i], &name));
748     if (!strcmp(name, field_name)) {
749       *op_field = input_fields[i];
750       return CEED_ERROR_SUCCESS;
751     }
752   }
753 
754   for (CeedInt i = 0; i < num_output_fields; i++) {
755     CeedCall(CeedOperatorFieldGetName(output_fields[i], &name));
756     if (!strcmp(name, field_name)) {
757       *op_field = output_fields[i];
758       return CEED_ERROR_SUCCESS;
759     }
760   }
761 
762   bool has_name = op->name;
763   // LCOV_EXCL_START
764   return CeedError(op->ceed, CEED_ERROR_MINOR, "The field \"%s\" not found in CeedOperator%s%s%s.\n", field_name, has_name ? " \"" : "",
765                    has_name ? op->name : "", has_name ? "\"" : "");
766   // LCOV_EXCL_STOP
767 }
768 
769 /**
770   @brief Get the name of a CeedOperatorField
771 
772   @param[in]  op_field    CeedOperatorField
773   @param[out] field_name  Variable to store the field name
774 
775   @return An error code: 0 - success, otherwise - failure
776 
777   @ref Advanced
778 **/
779 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
780   *field_name = (char *)op_field->field_name;
781   return CEED_ERROR_SUCCESS;
782 }
783 
784 /**
785   @brief Get the CeedElemRestriction of a CeedOperatorField
786 
787   @param[in]  op_field CeedOperatorField
788   @param[out] rstr     Variable to store CeedElemRestriction
789 
790   @return An error code: 0 - success, otherwise - failure
791 
792   @ref Advanced
793 **/
794 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field, CeedElemRestriction *rstr) {
795   *rstr = op_field->elem_rstr;
796   return CEED_ERROR_SUCCESS;
797 }
798 
799 /**
800   @brief Get the CeedBasis of a CeedOperatorField
801 
802   @param[in]  op_field CeedOperatorField
803   @param[out] basis    Variable to store CeedBasis
804 
805   @return An error code: 0 - success, otherwise - failure
806 
807   @ref Advanced
808 **/
809 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
810   *basis = op_field->basis;
811   return CEED_ERROR_SUCCESS;
812 }
813 
814 /**
815   @brief Get the CeedVector of a CeedOperatorField
816 
817   @param[in]  op_field CeedOperatorField
818   @param[out] vec      Variable to store CeedVector
819 
820   @return An error code: 0 - success, otherwise - failure
821 
822   @ref Advanced
823 **/
824 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
825   *vec = op_field->vec;
826   return CEED_ERROR_SUCCESS;
827 }
828 
829 /**
830   @brief Add a sub-operator to a composite CeedOperator
831 
832   @param[in,out] composite_op Composite CeedOperator
833   @param[in]     sub_op       Sub-operator CeedOperator
834 
835   @return An error code: 0 - success, otherwise - failure
836 
837   @ref User
838  */
839 int CeedCompositeOperatorAddSub(CeedOperator composite_op, CeedOperator sub_op) {
840   CeedCheck(composite_op->is_composite, composite_op->ceed, CEED_ERROR_MINOR, "CeedOperator is not a composite operator");
841   CeedCheck(composite_op->num_suboperators < CEED_COMPOSITE_MAX, composite_op->ceed, CEED_ERROR_UNSUPPORTED, "Cannot add additional sub-operators");
842   CeedCheck(!composite_op->is_immutable, composite_op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
843 
844   {
845     CeedSize input_size, output_size;
846     CeedCall(CeedOperatorGetActiveVectorLengths(sub_op, &input_size, &output_size));
847     if (composite_op->input_size == -1) composite_op->input_size = input_size;
848     if (composite_op->output_size == -1) composite_op->output_size = output_size;
849     // Note, a size of -1 means no active vector restriction set, so no incompatibility
850     CeedCheck((input_size == -1 || input_size == composite_op->input_size) && (output_size == -1 || output_size == composite_op->output_size),
851               composite_op->ceed, CEED_ERROR_MAJOR,
852               "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of "
853               "shape (%td, %td)",
854               composite_op->input_size, composite_op->output_size, input_size, output_size);
855   }
856 
857   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
858   CeedCall(CeedOperatorReference(sub_op));
859   composite_op->num_suboperators++;
860   return CEED_ERROR_SUCCESS;
861 }
862 
863 /**
864   @brief Get the number of sub_operators associated with a CeedOperator
865 
866   @param[in]  op               CeedOperator
867   @param[out] num_suboperators Variable to store number of sub_operators
868 
869   @return An error code: 0 - success, otherwise - failure
870 
871   @ref Backend
872 **/
873 int CeedCompositeOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
874   CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator");
875   *num_suboperators = op->num_suboperators;
876   return CEED_ERROR_SUCCESS;
877 }
878 
879 /**
880   @brief Get the list of sub_operators associated with a CeedOperator
881 
882   @param op                  CeedOperator
883   @param[out] sub_operators  Variable to store list of sub_operators
884 
885   @return An error code: 0 - success, otherwise - failure
886 
887   @ref Backend
888 **/
889 int CeedCompositeOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
890   CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator");
891   *sub_operators = op->sub_operators;
892   return CEED_ERROR_SUCCESS;
893 }
894 
895 /**
896   @brief Check if a CeedOperator is ready to be used.
897 
898   @param[in] op CeedOperator to check
899 
900   @return An error code: 0 - success, otherwise - failure
901 
902   @ref User
903 **/
904 int CeedOperatorCheckReady(CeedOperator op) {
905   Ceed ceed;
906   CeedCall(CeedOperatorGetCeed(op, &ceed));
907 
908   if (op->is_interface_setup) return CEED_ERROR_SUCCESS;
909 
910   CeedQFunction qf = op->qf;
911   if (op->is_composite) {
912     if (!op->num_suboperators) {
913       // Empty operator setup
914       op->input_size  = 0;
915       op->output_size = 0;
916     } else {
917       for (CeedInt i = 0; i < op->num_suboperators; i++) {
918         CeedCall(CeedOperatorCheckReady(op->sub_operators[i]));
919       }
920       // Sub-operators could be modified after adding to composite operator
921       // Need to verify no lvec incompatibility from any changes
922       CeedSize input_size, output_size;
923       CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
924     }
925   } else {
926     CeedCheck(op->num_fields > 0, ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
927     CeedCheck(op->num_fields == qf->num_input_fields + qf->num_output_fields, ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
928     CeedCheck(op->has_restriction, ceed, CEED_ERROR_INCOMPLETE, "At least one restriction required");
929     CeedCheck(op->num_qpts > 0, ceed, CEED_ERROR_INCOMPLETE,
930               "At least one non-collocated basis is required or the number of quadrature points must be set");
931   }
932 
933   // Flag as immutable and ready
934   op->is_interface_setup = true;
935   if (op->qf && op->qf != CEED_QFUNCTION_NONE) op->qf->is_immutable = true;
936   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE) op->dqf->is_immutable = true;
937   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE) op->dqfT->is_immutable = true;
938   return CEED_ERROR_SUCCESS;
939 }
940 
941 /**
942   @brief Get vector lengths for the active input and/or output vectors of a CeedOperator.
943 
944   Note: Lengths of -1 indicate that the CeedOperator does not have an active input and/or output.
945 
946   @param[in]  op          CeedOperator
947   @param[out] input_size  Variable to store active input vector length, or NULL
948   @param[out] output_size Variable to store active output vector length, or NULL
949 
950   @return An error code: 0 - success, otherwise - failure
951 
952   @ref User
953 **/
954 int CeedOperatorGetActiveVectorLengths(CeedOperator op, CeedSize *input_size, CeedSize *output_size) {
955   bool is_composite;
956 
957   if (input_size) *input_size = op->input_size;
958   if (output_size) *output_size = op->output_size;
959 
960   CeedCall(CeedOperatorIsComposite(op, &is_composite));
961   if (is_composite && (op->input_size == -1 || op->output_size == -1)) {
962     for (CeedInt i = 0; i < op->num_suboperators; i++) {
963       CeedSize sub_input_size, sub_output_size;
964       CeedCall(CeedOperatorGetActiveVectorLengths(op->sub_operators[i], &sub_input_size, &sub_output_size));
965       if (op->input_size == -1) op->input_size = sub_input_size;
966       if (op->output_size == -1) op->output_size = sub_output_size;
967       // Note, a size of -1 means no active vector restriction set, so no incompatibility
968       CeedCheck((sub_input_size == -1 || sub_input_size == op->input_size) && (sub_output_size == -1 || sub_output_size == op->output_size), op->ceed,
969                 CEED_ERROR_MAJOR,
970                 "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of "
971                 "shape (%td, %td)",
972                 op->input_size, op->output_size, input_size, output_size);
973     }
974   }
975 
976   return CEED_ERROR_SUCCESS;
977 }
978 
979 /**
980   @brief Set reuse of CeedQFunction data in CeedOperatorLinearAssemble* functions.
981 
982   When `reuse_assembly_data = false` (default), the CeedQFunction associated with this CeedOperator is re-assembled every time a
983 `CeedOperatorLinearAssemble*` function is called.
984   When `reuse_assembly_data = true`, the CeedQFunction associated with this CeedOperator is reused between calls to
985 `CeedOperatorSetQFunctionAssemblyDataUpdated`.
986 
987   @param[in] op                  CeedOperator
988   @param[in] reuse_assembly_data Boolean flag setting assembly data reuse
989 
990   @return An error code: 0 - success, otherwise - failure
991 
992   @ref Advanced
993 **/
994 int CeedOperatorSetQFunctionAssemblyReuse(CeedOperator op, bool reuse_assembly_data) {
995   bool is_composite;
996 
997   CeedCall(CeedOperatorIsComposite(op, &is_composite));
998   if (is_composite) {
999     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1000       CeedCall(CeedOperatorSetQFunctionAssemblyReuse(op->sub_operators[i], reuse_assembly_data));
1001     }
1002   } else {
1003     CeedCall(CeedQFunctionAssemblyDataSetReuse(op->qf_assembled, reuse_assembly_data));
1004   }
1005 
1006   return CEED_ERROR_SUCCESS;
1007 }
1008 
1009 /**
1010   @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly.
1011 
1012   @param[in] op                CeedOperator
1013   @param[in] needs_data_update Boolean flag setting assembly data reuse
1014 
1015   @return An error code: 0 - success, otherwise - failure
1016 
1017   @ref Advanced
1018 **/
1019 int CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(CeedOperator op, bool needs_data_update) {
1020   bool is_composite;
1021 
1022   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1023   if (is_composite) {
1024     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1025       CeedCall(CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(op->sub_operators[i], needs_data_update));
1026     }
1027   } else {
1028     CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, needs_data_update));
1029   }
1030 
1031   return CEED_ERROR_SUCCESS;
1032 }
1033 
1034 /**
1035   @brief Set the number of quadrature points associated with a CeedOperator.
1036 
1037   This should be used when creating a CeedOperator where every field has a collocated basis.
1038   This function cannot be used for composite CeedOperators.
1039 
1040   @param[in,out] op       CeedOperator
1041   @param[in]     num_qpts Number of quadrature points to set
1042 
1043   @return An error code: 0 - success, otherwise - failure
1044 
1045   @ref Advanced
1046 **/
1047 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
1048   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
1049   CeedCheck(op->num_qpts == 0, op->ceed, CEED_ERROR_MINOR, "Number of quadrature points already defined");
1050   CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
1051   op->num_qpts = num_qpts;
1052   return CEED_ERROR_SUCCESS;
1053 }
1054 
1055 /**
1056   @brief Set name of CeedOperator for CeedOperatorView output
1057 
1058   @param[in,out] op   CeedOperator
1059   @param[in]     name Name to set, or NULL to remove previously set name
1060 
1061   @return An error code: 0 - success, otherwise - failure
1062 
1063   @ref User
1064 **/
1065 int CeedOperatorSetName(CeedOperator op, const char *name) {
1066   char  *name_copy;
1067   size_t name_len = name ? strlen(name) : 0;
1068 
1069   CeedCall(CeedFree(&op->name));
1070   if (name_len > 0) {
1071     CeedCall(CeedCalloc(name_len + 1, &name_copy));
1072     memcpy(name_copy, name, name_len);
1073     op->name = name_copy;
1074   }
1075 
1076   return CEED_ERROR_SUCCESS;
1077 }
1078 
1079 /**
1080   @brief View a CeedOperator
1081 
1082   @param[in] op     CeedOperator to view
1083   @param[in] stream Stream to write; typically stdout/stderr or a file
1084 
1085   @return Error code: 0 - success, otherwise - failure
1086 
1087   @ref User
1088 **/
1089 int CeedOperatorView(CeedOperator op, FILE *stream) {
1090   bool has_name = op->name;
1091 
1092   if (op->is_composite) {
1093     fprintf(stream, "Composite CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : "");
1094 
1095     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1096       has_name = op->sub_operators[i]->name;
1097       fprintf(stream, "  SubOperator %" CeedInt_FMT "%s%s:\n", i, has_name ? " - " : "", has_name ? op->sub_operators[i]->name : "");
1098       CeedCall(CeedOperatorSingleView(op->sub_operators[i], 1, stream));
1099     }
1100   } else {
1101     fprintf(stream, "CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : "");
1102     CeedCall(CeedOperatorSingleView(op, 0, stream));
1103   }
1104   return CEED_ERROR_SUCCESS;
1105 }
1106 
1107 /**
1108   @brief Get the Ceed associated with a CeedOperator
1109 
1110   @param[in]  op   CeedOperator
1111   @param[out] ceed Variable to store Ceed
1112 
1113   @return An error code: 0 - success, otherwise - failure
1114 
1115   @ref Advanced
1116 **/
1117 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
1118   *ceed = op->ceed;
1119   return CEED_ERROR_SUCCESS;
1120 }
1121 
1122 /**
1123   @brief Get the number of elements associated with a CeedOperator
1124 
1125   @param[in]  op       CeedOperator
1126   @param[out] num_elem Variable to store number of elements
1127 
1128   @return An error code: 0 - success, otherwise - failure
1129 
1130   @ref Advanced
1131 **/
1132 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1133   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
1134   *num_elem = op->num_elem;
1135   return CEED_ERROR_SUCCESS;
1136 }
1137 
1138 /**
1139   @brief Get the number of quadrature points associated with a CeedOperator
1140 
1141   @param[in]  op       CeedOperator
1142   @param[out] num_qpts Variable to store vector number of quadrature points
1143 
1144   @return An error code: 0 - success, otherwise - failure
1145 
1146   @ref Advanced
1147 **/
1148 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1149   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
1150   *num_qpts = op->num_qpts;
1151   return CEED_ERROR_SUCCESS;
1152 }
1153 
1154 /**
1155   @brief Estimate number of FLOPs required to apply CeedOperator on the active vector
1156 
1157   @param[in]  op    CeedOperator to estimate FLOPs for
1158   @param[out] flops Address of variable to hold FLOPs estimate
1159 
1160   @ref Backend
1161 **/
1162 int CeedOperatorGetFlopsEstimate(CeedOperator op, CeedSize *flops) {
1163   bool is_composite;
1164   CeedCall(CeedOperatorCheckReady(op));
1165 
1166   *flops = 0;
1167   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1168   if (is_composite) {
1169     CeedInt num_suboperators;
1170     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1171     CeedOperator *sub_operators;
1172     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1173 
1174     // FLOPs for each suboperator
1175     for (CeedInt i = 0; i < num_suboperators; i++) {
1176       CeedSize suboperator_flops;
1177       CeedCall(CeedOperatorGetFlopsEstimate(sub_operators[i], &suboperator_flops));
1178       *flops += suboperator_flops;
1179     }
1180   } else {
1181     CeedInt            num_input_fields, num_output_fields;
1182     CeedOperatorField *input_fields, *output_fields;
1183     CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields));
1184     CeedInt num_elem = 0;
1185     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
1186 
1187     // Input FLOPs
1188     for (CeedInt i = 0; i < num_input_fields; i++) {
1189       if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1190         CeedSize restr_flops, basis_flops;
1191 
1192         CeedCall(CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_rstr, CEED_NOTRANSPOSE, &restr_flops));
1193         *flops += restr_flops;
1194         CeedCall(CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE, op->qf->input_fields[i]->eval_mode, &basis_flops));
1195         *flops += basis_flops * num_elem;
1196       }
1197     }
1198     // QF FLOPs
1199     CeedInt  num_qpts;
1200     CeedSize qf_flops;
1201     CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts));
1202     CeedCall(CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops));
1203     *flops += num_elem * num_qpts * qf_flops;
1204     // Output FLOPs
1205     for (CeedInt i = 0; i < num_output_fields; i++) {
1206       if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1207         CeedSize restr_flops, basis_flops;
1208 
1209         CeedCall(CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_rstr, CEED_TRANSPOSE, &restr_flops));
1210         *flops += restr_flops;
1211         CeedCall(CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE, op->qf->output_fields[i]->eval_mode, &basis_flops));
1212         *flops += basis_flops * num_elem;
1213       }
1214     }
1215   }
1216 
1217   return CEED_ERROR_SUCCESS;
1218 }
1219 
1220 /**
1221   @brief Get CeedQFunction global context for a CeedOperator.
1222 
1223   The caller is responsible for destroying `ctx` returned from this function via `CeedQFunctionContextDestroy()`.
1224 
1225   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.
1226         This CeedQFunctionContext will be destroyed if `ctx` is the only reference to this CeedQFunctionContext.
1227 
1228   @param[in]  op  CeedOperator
1229   @param[out] ctx Variable to store CeedQFunctionContext
1230 
1231   @return An error code: 0 - success, otherwise - failure
1232 
1233   @ref Advanced
1234 **/
1235 int CeedOperatorGetContext(CeedOperator op, CeedQFunctionContext *ctx) {
1236   CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot retrieve QFunctionContext for composite operator");
1237   if (op->qf->ctx) CeedCall(CeedQFunctionContextReferenceCopy(op->qf->ctx, ctx));
1238   else *ctx = NULL;
1239   return CEED_ERROR_SUCCESS;
1240 }
1241 
1242 /**
1243   @brief Get label for a registered QFunctionContext field, or `NULL` if no field has been registered with this `field_name`.
1244 
1245   Fields are registered via `CeedQFunctionContextRegister*()` functions (eg. `CeedQFunctionContextRegisterDouble()`).
1246 
1247   @param[in]  op          CeedOperator
1248   @param[in]  field_name  Name of field to retrieve label
1249   @param[out] field_label Variable to field label
1250 
1251   @return An error code: 0 - success, otherwise - failure
1252 
1253   @ref User
1254 **/
1255 int CeedOperatorGetContextFieldLabel(CeedOperator op, const char *field_name, CeedContextFieldLabel *field_label) {
1256   bool is_composite;
1257   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1258 
1259   if (is_composite) {
1260     // Check if composite label already created
1261     for (CeedInt i = 0; i < op->num_context_labels; i++) {
1262       if (!strcmp(op->context_labels[i]->name, field_name)) {
1263         *field_label = op->context_labels[i];
1264         return CEED_ERROR_SUCCESS;
1265       }
1266     }
1267 
1268     // Create composite label if needed
1269     CeedInt               num_sub;
1270     CeedOperator         *sub_operators;
1271     CeedContextFieldLabel new_field_label;
1272 
1273     CeedCall(CeedCalloc(1, &new_field_label));
1274     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
1275     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1276     CeedCall(CeedCalloc(num_sub, &new_field_label->sub_labels));
1277     new_field_label->num_sub_labels = num_sub;
1278 
1279     bool label_found = false;
1280     for (CeedInt i = 0; i < num_sub; i++) {
1281       if (sub_operators[i]->qf->ctx) {
1282         CeedContextFieldLabel new_field_label_i;
1283         CeedCall(CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name, &new_field_label_i));
1284         if (new_field_label_i) {
1285           label_found                    = true;
1286           new_field_label->sub_labels[i] = new_field_label_i;
1287           new_field_label->name          = new_field_label_i->name;
1288           new_field_label->description   = new_field_label_i->description;
1289           if (new_field_label->type && new_field_label->type != new_field_label_i->type) {
1290             // LCOV_EXCL_START
1291             CeedCall(CeedFree(&new_field_label));
1292             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field types on sub-operator contexts. %s != %s",
1293                              CeedContextFieldTypes[new_field_label->type], CeedContextFieldTypes[new_field_label_i->type]);
1294             // LCOV_EXCL_STOP
1295           } else {
1296             new_field_label->type = new_field_label_i->type;
1297           }
1298           if (new_field_label->num_values != 0 && new_field_label->num_values != new_field_label_i->num_values) {
1299             // LCOV_EXCL_START
1300             CeedCall(CeedFree(&new_field_label));
1301             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field number of values on sub-operator contexts. %ld != %ld",
1302                              new_field_label->num_values, new_field_label_i->num_values);
1303             // LCOV_EXCL_STOP
1304           } else {
1305             new_field_label->num_values = new_field_label_i->num_values;
1306           }
1307         }
1308       }
1309     }
1310     if (!label_found) {
1311       // LCOV_EXCL_START
1312       CeedCall(CeedFree(&new_field_label->sub_labels));
1313       CeedCall(CeedFree(&new_field_label));
1314       *field_label = NULL;
1315       // LCOV_EXCL_STOP
1316     } else {
1317       *field_label = new_field_label;
1318     }
1319   } else {
1320     CeedCall(CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label));
1321   }
1322 
1323   // Set label in operator
1324   if (*field_label) {
1325     (*field_label)->from_op = true;
1326 
1327     // Move new composite label to operator
1328     if (op->num_context_labels == 0) {
1329       CeedCall(CeedCalloc(1, &op->context_labels));
1330       op->max_context_labels = 1;
1331     } else if (op->num_context_labels == op->max_context_labels) {
1332       CeedCall(CeedRealloc(2 * op->num_context_labels, &op->context_labels));
1333       op->max_context_labels *= 2;
1334     }
1335     op->context_labels[op->num_context_labels] = *field_label;
1336     op->num_context_labels++;
1337   }
1338 
1339   return CEED_ERROR_SUCCESS;
1340 }
1341 
1342 /**
1343   @brief Set QFunctionContext field holding double precision values.
1344 
1345   For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`.
1346 
1347   @param[in,out] op          CeedOperator
1348   @param[in]     field_label Label of field to set
1349   @param[in]     values      Values to set
1350 
1351   @return An error code: 0 - success, otherwise - failure
1352 
1353   @ref User
1354 **/
1355 int CeedOperatorSetContextDouble(CeedOperator op, CeedContextFieldLabel field_label, double *values) {
1356   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values);
1357 }
1358 
1359 /**
1360   @brief Get QFunctionContext field holding double precision values, read-only.
1361 
1362   For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`.
1363 
1364   @param[in]  op          CeedOperator
1365   @param[in]  field_label Label of field to get
1366   @param[out] num_values  Number of values in the field label
1367   @param[out] values      Pointer to context values
1368 
1369   @return An error code: 0 - success, otherwise - failure
1370 
1371   @ref User
1372 **/
1373 int CeedOperatorGetContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const double **values) {
1374   return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, num_values, values);
1375 }
1376 
1377 /**
1378   @brief Restore QFunctionContext field holding double precision values, read-only.
1379 
1380   @param[in]  op          CeedOperator
1381   @param[in]  field_label Label of field to restore
1382   @param[out] values      Pointer to context values
1383 
1384   @return An error code: 0 - success, otherwise - failure
1385 
1386   @ref User
1387 **/
1388 int CeedOperatorRestoreContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, const double **values) {
1389   return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values);
1390 }
1391 
1392 /**
1393   @brief Set QFunctionContext field holding int32 values.
1394 
1395   For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`.
1396 
1397   @param[in,out] op          CeedOperator
1398   @param[in]     field_label Label of field to set
1399   @param[in]     values      Values to set
1400 
1401   @return An error code: 0 - success, otherwise - failure
1402 
1403   @ref User
1404 **/
1405 int CeedOperatorSetContextInt32(CeedOperator op, CeedContextFieldLabel field_label, int *values) {
1406   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32, values);
1407 }
1408 
1409 /**
1410   @brief Get QFunctionContext field holding int32 values, read-only.
1411 
1412   For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`.
1413 
1414   @param[in]  op          CeedOperator
1415   @param[in]  field_label Label of field to get
1416   @param[out] num_values  Number of int32 values in `values`
1417   @param[out] values      Pointer to context values
1418 
1419   @return An error code: 0 - success, otherwise - failure
1420 
1421   @ref User
1422 **/
1423 int CeedOperatorGetContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const int **values) {
1424   return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, num_values, values);
1425 }
1426 
1427 /**
1428   @brief Restore QFunctionContext field holding int32 values, read-only.
1429 
1430   @param[in]  op          CeedOperator
1431   @param[in]  field_label Label of field to get
1432   @param[out] values      Pointer to context values
1433 
1434   @return An error code: 0 - success, otherwise - failure
1435 
1436   @ref User
1437 **/
1438 int CeedOperatorRestoreContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, const int **values) {
1439   return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, values);
1440 }
1441 
1442 /**
1443   @brief Apply CeedOperator to a vector
1444 
1445   This computes the action of the operator on the specified (active) input, yielding its (active) output.
1446   All inputs and outputs must be specified using CeedOperatorSetField().
1447 
1448   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1449 
1450   @param[in]  op      CeedOperator to apply
1451   @param[in]  in      CeedVector containing input state or @ref CEED_VECTOR_NONE if there are no active inputs
1452   @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
1453 active outputs
1454   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1455 
1456   @return An error code: 0 - success, otherwise - failure
1457 
1458   @ref User
1459 **/
1460 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) {
1461   CeedCall(CeedOperatorCheckReady(op));
1462 
1463   if (op->is_composite) {
1464     // Composite Operator
1465     if (op->ApplyComposite) {
1466       CeedCall(op->ApplyComposite(op, in, out, request));
1467     } else {
1468       CeedInt num_suboperators;
1469       CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1470       CeedOperator *sub_operators;
1471       CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1472 
1473       // Zero all output vectors
1474       if (out != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(out, 0.0));
1475       for (CeedInt i = 0; i < num_suboperators; i++) {
1476         for (CeedInt j = 0; j < sub_operators[i]->qf->num_output_fields; j++) {
1477           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1478           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1479             CeedCall(CeedVectorSetValue(vec, 0.0));
1480           }
1481         }
1482       }
1483       // Apply
1484       for (CeedInt i = 0; i < op->num_suboperators; i++) {
1485         CeedCall(CeedOperatorApplyAdd(op->sub_operators[i], in, out, request));
1486       }
1487     }
1488   } else {
1489     // Standard Operator
1490     if (op->Apply) {
1491       CeedCall(op->Apply(op, in, out, request));
1492     } else {
1493       // Zero all output vectors
1494       CeedQFunction qf = op->qf;
1495       for (CeedInt i = 0; i < qf->num_output_fields; i++) {
1496         CeedVector vec = op->output_fields[i]->vec;
1497         if (vec == CEED_VECTOR_ACTIVE) vec = out;
1498         if (vec != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(vec, 0.0));
1499       }
1500       // Apply
1501       if (op->num_elem) CeedCall(op->ApplyAdd(op, in, out, request));
1502     }
1503   }
1504   return CEED_ERROR_SUCCESS;
1505 }
1506 
1507 /**
1508   @brief Apply CeedOperator to a vector and add result to output vector
1509 
1510   This computes the action of the operator on the specified (active) input, yielding its (active) output.
1511   All inputs and outputs must be specified using CeedOperatorSetField().
1512 
1513   @param[in]  op      CeedOperator to apply
1514   @param[in]  in      CeedVector containing input state or NULL if there are no active inputs
1515   @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
1516   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1517 
1518   @return An error code: 0 - success, otherwise - failure
1519 
1520   @ref User
1521 **/
1522 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) {
1523   CeedCall(CeedOperatorCheckReady(op));
1524 
1525   if (op->is_composite) {
1526     // Composite Operator
1527     if (op->ApplyAddComposite) {
1528       CeedCall(op->ApplyAddComposite(op, in, out, request));
1529     } else {
1530       CeedInt num_suboperators;
1531       CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1532       CeedOperator *sub_operators;
1533       CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1534 
1535       for (CeedInt i = 0; i < num_suboperators; i++) {
1536         CeedCall(CeedOperatorApplyAdd(sub_operators[i], in, out, request));
1537       }
1538     }
1539   } else if (op->num_elem) {
1540     // Standard Operator
1541     CeedCall(op->ApplyAdd(op, in, out, request));
1542   }
1543   return CEED_ERROR_SUCCESS;
1544 }
1545 
1546 /**
1547   @brief Destroy a CeedOperator
1548 
1549   @param[in,out] op CeedOperator to destroy
1550 
1551   @return An error code: 0 - success, otherwise - failure
1552 
1553   @ref User
1554 **/
1555 int CeedOperatorDestroy(CeedOperator *op) {
1556   if (!*op || --(*op)->ref_count > 0) {
1557     *op = NULL;
1558     return CEED_ERROR_SUCCESS;
1559   }
1560   if ((*op)->Destroy) CeedCall((*op)->Destroy(*op));
1561   CeedCall(CeedDestroy(&(*op)->ceed));
1562   // Free fields
1563   for (CeedInt i = 0; i < (*op)->num_fields; i++) {
1564     if ((*op)->input_fields[i]) {
1565       if ((*op)->input_fields[i]->elem_rstr != CEED_ELEMRESTRICTION_NONE) {
1566         CeedCall(CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_rstr));
1567       }
1568       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1569         CeedCall(CeedBasisDestroy(&(*op)->input_fields[i]->basis));
1570       }
1571       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->input_fields[i]->vec != CEED_VECTOR_NONE) {
1572         CeedCall(CeedVectorDestroy(&(*op)->input_fields[i]->vec));
1573       }
1574       CeedCall(CeedFree(&(*op)->input_fields[i]->field_name));
1575       CeedCall(CeedFree(&(*op)->input_fields[i]));
1576     }
1577   }
1578   for (CeedInt i = 0; i < (*op)->num_fields; i++) {
1579     if ((*op)->output_fields[i]) {
1580       CeedCall(CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_rstr));
1581       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1582         CeedCall(CeedBasisDestroy(&(*op)->output_fields[i]->basis));
1583       }
1584       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->output_fields[i]->vec != CEED_VECTOR_NONE) {
1585         CeedCall(CeedVectorDestroy(&(*op)->output_fields[i]->vec));
1586       }
1587       CeedCall(CeedFree(&(*op)->output_fields[i]->field_name));
1588       CeedCall(CeedFree(&(*op)->output_fields[i]));
1589     }
1590   }
1591   // Destroy sub_operators
1592   for (CeedInt i = 0; i < (*op)->num_suboperators; i++) {
1593     if ((*op)->sub_operators[i]) {
1594       CeedCall(CeedOperatorDestroy(&(*op)->sub_operators[i]));
1595     }
1596   }
1597   CeedCall(CeedQFunctionDestroy(&(*op)->qf));
1598   CeedCall(CeedQFunctionDestroy(&(*op)->dqf));
1599   CeedCall(CeedQFunctionDestroy(&(*op)->dqfT));
1600   // Destroy any composite labels
1601   if ((*op)->is_composite) {
1602     for (CeedInt i = 0; i < (*op)->num_context_labels; i++) {
1603       CeedCall(CeedFree(&(*op)->context_labels[i]->sub_labels));
1604       CeedCall(CeedFree(&(*op)->context_labels[i]));
1605     }
1606   }
1607   CeedCall(CeedFree(&(*op)->context_labels));
1608 
1609   // Destroy fallback
1610   CeedCall(CeedOperatorDestroy(&(*op)->op_fallback));
1611 
1612   // Destroy assembly data
1613   CeedCall(CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled));
1614   CeedCall(CeedOperatorAssemblyDataDestroy(&(*op)->op_assembled));
1615 
1616   CeedCall(CeedFree(&(*op)->input_fields));
1617   CeedCall(CeedFree(&(*op)->output_fields));
1618   CeedCall(CeedFree(&(*op)->sub_operators));
1619   CeedCall(CeedFree(&(*op)->name));
1620   CeedCall(CeedFree(op));
1621   return CEED_ERROR_SUCCESS;
1622 }
1623 
1624 /// @}
1625