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