xref: /libCEED/interface/ceed-operator.c (revision 512bb800c3b780a95e9f25e68d5c95fed17fce79)
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 
690            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.
691              This CeedOperator will be destroyed if `op_copy` is the only reference to this CeedOperator.
692 
693   @param[in]  op         CeedOperator to copy reference to
694   @param[in,out] op_copy Variable to store copied reference
695 
696   @return An error code: 0 - success, otherwise - failure
697 
698   @ref User
699 **/
700 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
701   CeedCall(CeedOperatorReference(op));
702   CeedCall(CeedOperatorDestroy(op_copy));
703   *op_copy = op;
704   return CEED_ERROR_SUCCESS;
705 }
706 
707 /**
708   @brief Provide a field to a CeedOperator for use by its CeedQFunction.
709 
710   This function is used to specify both active and passive fields to a CeedOperator.
711   For passive fields, a vector @arg v must be provided.
712   Passive fields can inputs or outputs (updated in-place when operator is applied).
713 
714   Active fields must be specified using this function, but their data (in a CeedVector) is passed in CeedOperatorApply().
715   There can be at most one active input CeedVector and at most one active output CeedVector passed to CeedOperatorApply().
716 
717   @param[in,out] op         CeedOperator on which to provide the field
718   @param[in]     field_name Name of the field (to be matched with the name used by CeedQFunction)
719   @param[in]     r          CeedElemRestriction
720   @param[in]     b          CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED if collocated with quadrature points
721   @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
722 CEED_EVAL_WEIGHT in the QFunction
723 
724   @return An error code: 0 - success, otherwise - failure
725 
726   @ref User
727 **/
728 int CeedOperatorSetField(CeedOperator op, const char *field_name, CeedElemRestriction r, CeedBasis b, CeedVector v) {
729   if (op->is_composite) {
730     // LCOV_EXCL_START
731     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot add field to composite operator.");
732     // LCOV_EXCL_STOP
733   }
734   if (op->is_immutable) {
735     // LCOV_EXCL_START
736     return CeedError(op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
737     // LCOV_EXCL_STOP
738   }
739   if (!r) {
740     // LCOV_EXCL_START
741     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "ElemRestriction r for field \"%s\" must be non-NULL.", field_name);
742     // LCOV_EXCL_STOP
743   }
744   if (!b) {
745     // LCOV_EXCL_START
746     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Basis b for field \"%s\" must be non-NULL.", field_name);
747     // LCOV_EXCL_STOP
748   }
749   if (!v) {
750     // LCOV_EXCL_START
751     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Vector v for field \"%s\" must be non-NULL.", field_name);
752     // LCOV_EXCL_STOP
753   }
754 
755   CeedInt num_elem;
756   CeedCall(CeedElemRestrictionGetNumElements(r, &num_elem));
757   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction && op->num_elem != num_elem) {
758     // LCOV_EXCL_START
759     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
760                      "ElemRestriction with %" CeedInt_FMT " elements incompatible with prior %" CeedInt_FMT " elements", num_elem, op->num_elem);
761     // LCOV_EXCL_STOP
762   }
763 
764   CeedInt num_qpts = 0;
765   if (b != CEED_BASIS_COLLOCATED) {
766     CeedCall(CeedBasisGetNumQuadraturePoints(b, &num_qpts));
767     if (op->num_qpts && op->num_qpts != num_qpts) {
768       // LCOV_EXCL_START
769       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
770                        "Basis with %" CeedInt_FMT " quadrature points incompatible with prior %" CeedInt_FMT " points", num_qpts, op->num_qpts);
771       // LCOV_EXCL_STOP
772     }
773   }
774   CeedQFunctionField qf_field;
775   CeedOperatorField *op_field;
776   bool               is_input = true;
777   for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
778     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
779       qf_field = op->qf->input_fields[i];
780       op_field = &op->input_fields[i];
781       goto found;
782     }
783   }
784   is_input = false;
785   for (CeedInt i = 0; i < op->qf->num_output_fields; i++) {
786     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
787       qf_field = op->qf->output_fields[i];
788       op_field = &op->output_fields[i];
789       goto found;
790     }
791   }
792   // LCOV_EXCL_START
793   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE, "QFunction has no knowledge of field '%s'", field_name);
794   // LCOV_EXCL_STOP
795 found:
796   CeedCall(CeedOperatorCheckField(op->ceed, qf_field, r, b));
797   CeedCall(CeedCalloc(1, op_field));
798 
799   if (v == CEED_VECTOR_ACTIVE) {
800     CeedSize l_size;
801     CeedCall(CeedElemRestrictionGetLVectorSize(r, &l_size));
802     if (is_input) {
803       if (op->input_size == -1) op->input_size = l_size;
804       if (l_size != op->input_size) {
805         // LCOV_EXCL_START
806         return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size, op->input_size);
807         // LCOV_EXCL_STOP
808       }
809     } else {
810       if (op->output_size == -1) op->output_size = l_size;
811       if (l_size != op->output_size) {
812         // LCOV_EXCL_START
813         return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size, op->output_size);
814         // LCOV_EXCL_STOP
815       }
816     }
817   }
818 
819   (*op_field)->vec = v;
820   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
821     CeedCall(CeedVectorReference(v));
822   }
823 
824   (*op_field)->elem_rstr = r;
825   CeedCall(CeedElemRestrictionReference(r));
826   if (r != CEED_ELEMRESTRICTION_NONE) {
827     op->num_elem        = num_elem;
828     op->has_restriction = true;  // Restriction set, but num_elem may be 0
829   }
830 
831   (*op_field)->basis = b;
832   if (b != CEED_BASIS_COLLOCATED) {
833     if (!op->num_qpts) {
834       CeedCall(CeedOperatorSetNumQuadraturePoints(op, num_qpts));
835     }
836     CeedCall(CeedBasisReference(b));
837   }
838 
839   op->num_fields += 1;
840   CeedCall(CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name));
841   return CEED_ERROR_SUCCESS;
842 }
843 
844 /**
845   @brief Get the CeedOperatorFields of a CeedOperator
846 
847   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
848 
849   @param[in]  op                CeedOperator
850   @param[out] num_input_fields  Variable to store number of input fields
851   @param[out] input_fields      Variable to store input_fields
852   @param[out] num_output_fields Variable to store number of output fields
853   @param[out] output_fields     Variable to store output_fields
854 
855   @return An error code: 0 - success, otherwise - failure
856 
857   @ref Advanced
858 **/
859 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields, CeedOperatorField **input_fields, CeedInt *num_output_fields,
860                           CeedOperatorField **output_fields) {
861   if (op->is_composite) {
862     // LCOV_EXCL_START
863     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
864     // LCOV_EXCL_STOP
865   }
866   CeedCall(CeedOperatorCheckReady(op));
867 
868   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
869   if (input_fields) *input_fields = op->input_fields;
870   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
871   if (output_fields) *output_fields = op->output_fields;
872   return CEED_ERROR_SUCCESS;
873 }
874 
875 /**
876   @brief Get a CeedOperatorField of an CeedOperator from its name
877 
878   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
879 
880   @param[in]  op         CeedOperator
881   @param[in]  field_name Name of desired CeedOperatorField
882   @param[out] op_field   CeedOperatorField corresponding to the name
883 
884   @return An error code: 0 - success, otherwise - failure
885 
886   @ref Advanced
887 **/
888 int CeedOperatorGetFieldByName(CeedOperator op, const char *field_name, CeedOperatorField *op_field) {
889   CeedInt            num_input_fields, num_output_fields;
890   CeedOperatorField *input_fields, *output_fields;
891   char              *name;
892   CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields));
893 
894   for (CeedInt i = 0; i < num_input_fields; i++) {
895     CeedCall(CeedOperatorFieldGetName(input_fields[i], &name));
896     if (!strcmp(name, field_name)) {
897       *op_field = input_fields[i];
898       return CEED_ERROR_SUCCESS;
899     }
900   }
901 
902   for (CeedInt i = 0; i < num_output_fields; i++) {
903     CeedCall(CeedOperatorFieldGetName(output_fields[i], &name));
904     if (!strcmp(name, field_name)) {
905       *op_field = output_fields[i];
906       return CEED_ERROR_SUCCESS;
907     }
908   }
909 
910   bool has_name = op->name;
911   // LCOV_EXCL_START
912   return CeedError(op->ceed, CEED_ERROR_MINOR, "The field \"%s\" not found in CeedOperator%s%s%s.\n", field_name, has_name ? " \"" : "",
913                    has_name ? op->name : "", has_name ? "\"" : "");
914   // LCOV_EXCL_STOP
915 }
916 
917 /**
918   @brief Get the name of a CeedOperatorField
919 
920   @param[in]  op_field    CeedOperatorField
921   @param[out] field_name  Variable to store the field name
922 
923   @return An error code: 0 - success, otherwise - failure
924 
925   @ref Advanced
926 **/
927 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
928   *field_name = (char *)op_field->field_name;
929   return CEED_ERROR_SUCCESS;
930 }
931 
932 /**
933   @brief Get the CeedElemRestriction of a CeedOperatorField
934 
935   @param[in]  op_field CeedOperatorField
936   @param[out] rstr     Variable to store CeedElemRestriction
937 
938   @return An error code: 0 - success, otherwise - failure
939 
940   @ref Advanced
941 **/
942 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field, CeedElemRestriction *rstr) {
943   *rstr = op_field->elem_rstr;
944   return CEED_ERROR_SUCCESS;
945 }
946 
947 /**
948   @brief Get the CeedBasis of a CeedOperatorField
949 
950   @param[in]  op_field CeedOperatorField
951   @param[out] basis    Variable to store CeedBasis
952 
953   @return An error code: 0 - success, otherwise - failure
954 
955   @ref Advanced
956 **/
957 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
958   *basis = op_field->basis;
959   return CEED_ERROR_SUCCESS;
960 }
961 
962 /**
963   @brief Get the CeedVector of a CeedOperatorField
964 
965   @param[in]  op_field CeedOperatorField
966   @param[out] vec      Variable to store CeedVector
967 
968   @return An error code: 0 - success, otherwise - failure
969 
970   @ref Advanced
971 **/
972 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
973   *vec = op_field->vec;
974   return CEED_ERROR_SUCCESS;
975 }
976 
977 /**
978   @brief Add a sub-operator to a composite CeedOperator
979 
980   @param[in,out] composite_op Composite CeedOperator
981   @param[in]     sub_op       Sub-operator CeedOperator
982 
983   @return An error code: 0 - success, otherwise - failure
984 
985   @ref User
986  */
987 int CeedCompositeOperatorAddSub(CeedOperator composite_op, CeedOperator sub_op) {
988   if (!composite_op->is_composite) {
989     // LCOV_EXCL_START
990     return CeedError(composite_op->ceed, CEED_ERROR_MINOR, "CeedOperator is not a composite operator");
991     // LCOV_EXCL_STOP
992   }
993 
994   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX) {
995     // LCOV_EXCL_START
996     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED, "Cannot add additional sub-operators");
997     // LCOV_EXCL_STOP
998   }
999   if (composite_op->is_immutable) {
1000     // LCOV_EXCL_START
1001     return CeedError(composite_op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
1002     // LCOV_EXCL_STOP
1003   }
1004 
1005   {
1006     CeedSize input_size, output_size;
1007     CeedCall(CeedOperatorGetActiveVectorLengths(sub_op, &input_size, &output_size));
1008     if (composite_op->input_size == -1) composite_op->input_size = input_size;
1009     if (composite_op->output_size == -1) composite_op->output_size = output_size;
1010     // Note, a size of -1 means no active vector restriction set, so no incompatibility
1011     if ((input_size != -1 && input_size != composite_op->input_size) || (output_size != -1 && output_size != composite_op->output_size)) {
1012       // LCOV_EXCL_START
1013       return CeedError(composite_op->ceed, CEED_ERROR_MAJOR,
1014                        "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of "
1015                        "shape (%td, %td)",
1016                        composite_op->input_size, composite_op->output_size, input_size, output_size);
1017       // LCOV_EXCL_STOP
1018     }
1019   }
1020 
1021   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
1022   CeedCall(CeedOperatorReference(sub_op));
1023   composite_op->num_suboperators++;
1024   return CEED_ERROR_SUCCESS;
1025 }
1026 
1027 /**
1028   @brief Get the number of sub_operators associated with a CeedOperator
1029 
1030   @param[in]  op               CeedOperator
1031   @param[out] num_suboperators Variable to store number of sub_operators
1032 
1033   @return An error code: 0 - success, otherwise - failure
1034 
1035   @ref Backend
1036 **/
1037 
1038 int CeedCompositeOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
1039   if (!op->is_composite) {
1040     // LCOV_EXCL_START
1041     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
1042     // LCOV_EXCL_STOP
1043   }
1044   *num_suboperators = op->num_suboperators;
1045   return CEED_ERROR_SUCCESS;
1046 }
1047 
1048 /**
1049   @brief Get the list of sub_operators associated with a CeedOperator
1050 
1051   @param op                  CeedOperator
1052   @param[out] sub_operators  Variable to store list of sub_operators
1053 
1054   @return An error code: 0 - success, otherwise - failure
1055 
1056   @ref Backend
1057 **/
1058 
1059 int CeedCompositeOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
1060   if (!op->is_composite) {
1061     // LCOV_EXCL_START
1062     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
1063     // LCOV_EXCL_STOP
1064   }
1065   *sub_operators = op->sub_operators;
1066   return CEED_ERROR_SUCCESS;
1067 }
1068 
1069 /**
1070   @brief Check if a CeedOperator is ready to be used.
1071 
1072   @param[in] op CeedOperator to check
1073 
1074   @return An error code: 0 - success, otherwise - failure
1075 
1076   @ref User
1077 **/
1078 int CeedOperatorCheckReady(CeedOperator op) {
1079   Ceed ceed;
1080   CeedCall(CeedOperatorGetCeed(op, &ceed));
1081 
1082   if (op->is_interface_setup) return CEED_ERROR_SUCCESS;
1083 
1084   CeedQFunction qf = op->qf;
1085   if (op->is_composite) {
1086     if (!op->num_suboperators) {
1087       // Empty operator setup
1088       op->input_size  = 0;
1089       op->output_size = 0;
1090     } else {
1091       for (CeedInt i = 0; i < op->num_suboperators; i++) {
1092         CeedCall(CeedOperatorCheckReady(op->sub_operators[i]));
1093       }
1094       // Sub-operators could be modified after adding to composite operator
1095       // Need to verify no lvec incompatibility from any changes
1096       CeedSize input_size, output_size;
1097       CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
1098     }
1099   } else {
1100     if (op->num_fields == 0) {
1101       // LCOV_EXCL_START
1102       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
1103       // LCOV_EXCL_STOP
1104     }
1105     if (op->num_fields < qf->num_input_fields + qf->num_output_fields) {
1106       // LCOV_EXCL_START
1107       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
1108       // LCOV_EXCL_STOP
1109     }
1110     if (!op->has_restriction) {
1111       // LCOV_EXCL_START
1112       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "At least one restriction required");
1113       // LCOV_EXCL_STOP
1114     }
1115     if (op->num_qpts == 0) {
1116       // LCOV_EXCL_START
1117       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "At least one non-collocated basis is required or the number of quadrature points must be set");
1118       // LCOV_EXCL_STOP
1119     }
1120   }
1121 
1122   // Flag as immutable and ready
1123   op->is_interface_setup = true;
1124   if (op->qf && op->qf != CEED_QFUNCTION_NONE) op->qf->is_immutable = true;
1125   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE) op->dqf->is_immutable = true;
1126   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE) op->dqfT->is_immutable = true;
1127   return CEED_ERROR_SUCCESS;
1128 }
1129 
1130 /**
1131   @brief Get vector lengths for the active input and/or output vectors of a CeedOperator.
1132            Note: Lengths of -1 indicate that the CeedOperator does not have an active input and/or output.
1133 
1134   @param[in]  op          CeedOperator
1135   @param[out] input_size  Variable to store active input vector length, or NULL
1136   @param[out] output_size Variable to store active output vector length, or NULL
1137 
1138   @return An error code: 0 - success, otherwise - failure
1139 
1140   @ref User
1141 **/
1142 int CeedOperatorGetActiveVectorLengths(CeedOperator op, CeedSize *input_size, CeedSize *output_size) {
1143   bool is_composite;
1144 
1145   if (input_size) *input_size = op->input_size;
1146   if (output_size) *output_size = op->output_size;
1147 
1148   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1149   if (is_composite && (op->input_size == -1 || op->output_size == -1)) {
1150     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1151       CeedSize sub_input_size, sub_output_size;
1152       CeedCall(CeedOperatorGetActiveVectorLengths(op->sub_operators[i], &sub_input_size, &sub_output_size));
1153       if (op->input_size == -1) op->input_size = sub_input_size;
1154       if (op->output_size == -1) op->output_size = sub_output_size;
1155       // Note, a size of -1 means no active vector restriction set, so no incompatibility
1156       if ((sub_input_size != -1 && sub_input_size != op->input_size) || (sub_output_size != -1 && sub_output_size != op->output_size)) {
1157         // LCOV_EXCL_START
1158         return CeedError(op->ceed, CEED_ERROR_MAJOR,
1159                          "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of "
1160                          "shape (%td, %td)",
1161                          op->input_size, op->output_size, input_size, output_size);
1162         // LCOV_EXCL_STOP
1163       }
1164     }
1165   }
1166 
1167   return CEED_ERROR_SUCCESS;
1168 }
1169 
1170 /**
1171   @brief Set reuse of CeedQFunction data in CeedOperatorLinearAssemble* functions.
1172            When `reuse_assembly_data = false` (default), the CeedQFunction associated with this CeedOperator is re-assembled every time a
1173 `CeedOperatorLinearAssemble*` function is called. When `reuse_assembly_data = true`, the CeedQFunction associated with this CeedOperator is reused
1174 between calls to `CeedOperatorSetQFunctionAssemblyDataUpdated`.
1175 
1176   @param[in] op                  CeedOperator
1177   @param[in] reuse_assembly_data Boolean flag setting assembly data reuse
1178 
1179   @return An error code: 0 - success, otherwise - failure
1180 
1181   @ref Advanced
1182 **/
1183 int CeedOperatorSetQFunctionAssemblyReuse(CeedOperator op, bool reuse_assembly_data) {
1184   bool is_composite;
1185 
1186   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1187   if (is_composite) {
1188     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1189       CeedCall(CeedOperatorSetQFunctionAssemblyReuse(op->sub_operators[i], reuse_assembly_data));
1190     }
1191   } else {
1192     CeedCall(CeedQFunctionAssemblyDataSetReuse(op->qf_assembled, reuse_assembly_data));
1193   }
1194 
1195   return CEED_ERROR_SUCCESS;
1196 }
1197 
1198 /**
1199   @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly.
1200 
1201   @param[in] op                CeedOperator
1202   @param[in] needs_data_update Boolean flag setting assembly data reuse
1203 
1204   @return An error code: 0 - success, otherwise - failure
1205 
1206   @ref Advanced
1207 **/
1208 int CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(CeedOperator op, bool needs_data_update) {
1209   bool is_composite;
1210 
1211   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1212   if (is_composite) {
1213     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1214       CeedCall(CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(op->sub_operators[i], needs_data_update));
1215     }
1216   } else {
1217     CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, needs_data_update));
1218   }
1219 
1220   return CEED_ERROR_SUCCESS;
1221 }
1222 
1223 /**
1224   @brief Set the number of quadrature points associated with a CeedOperator.
1225            This should be used when creating a CeedOperator where every field has a collocated basis.
1226            This function cannot be used for composite CeedOperators.
1227 
1228   @param[in,out] op       CeedOperator
1229   @param[in]     num_qpts Number of quadrature points to set
1230 
1231   @return An error code: 0 - success, otherwise - failure
1232 
1233   @ref Advanced
1234 **/
1235 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
1236   if (op->is_composite) {
1237     // LCOV_EXCL_START
1238     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
1239     // LCOV_EXCL_STOP
1240   }
1241   if (op->num_qpts) {
1242     // LCOV_EXCL_START
1243     return CeedError(op->ceed, CEED_ERROR_MINOR, "Number of quadrature points already defined");
1244     // LCOV_EXCL_STOP
1245   }
1246   if (op->is_immutable) {
1247     // LCOV_EXCL_START
1248     return CeedError(op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable");
1249     // LCOV_EXCL_STOP
1250   }
1251   op->num_qpts = num_qpts;
1252   return CEED_ERROR_SUCCESS;
1253 }
1254 
1255 /**
1256   @brief Set name of CeedOperator for CeedOperatorView output
1257 
1258   @param[in,out] op   CeedOperator
1259   @param[in]     name Name to set, or NULL to remove previously set name
1260 
1261   @return An error code: 0 - success, otherwise - failure
1262 
1263   @ref User
1264 **/
1265 int CeedOperatorSetName(CeedOperator op, const char *name) {
1266   char  *name_copy;
1267   size_t name_len = name ? strlen(name) : 0;
1268 
1269   CeedCall(CeedFree(&op->name));
1270   if (name_len > 0) {
1271     CeedCall(CeedCalloc(name_len + 1, &name_copy));
1272     memcpy(name_copy, name, name_len);
1273     op->name = name_copy;
1274   }
1275 
1276   return CEED_ERROR_SUCCESS;
1277 }
1278 
1279 /**
1280   @brief View a CeedOperator
1281 
1282   @param[in] op     CeedOperator to view
1283   @param[in] stream Stream to write; typically stdout/stderr or a file
1284 
1285   @return Error code: 0 - success, otherwise - failure
1286 
1287   @ref User
1288 **/
1289 int CeedOperatorView(CeedOperator op, FILE *stream) {
1290   bool has_name = op->name;
1291 
1292   if (op->is_composite) {
1293     fprintf(stream, "Composite CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : "");
1294 
1295     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1296       has_name = op->sub_operators[i]->name;
1297       fprintf(stream, "  SubOperator %" CeedInt_FMT "%s%s:\n", i, has_name ? " - " : "", has_name ? op->sub_operators[i]->name : "");
1298       CeedCall(CeedOperatorSingleView(op->sub_operators[i], 1, stream));
1299     }
1300   } else {
1301     fprintf(stream, "CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : "");
1302     CeedCall(CeedOperatorSingleView(op, 0, stream));
1303   }
1304   return CEED_ERROR_SUCCESS;
1305 }
1306 
1307 /**
1308   @brief Get the Ceed associated with a CeedOperator
1309 
1310   @param[in]  op   CeedOperator
1311   @param[out] ceed Variable to store Ceed
1312 
1313   @return An error code: 0 - success, otherwise - failure
1314 
1315   @ref Advanced
1316 **/
1317 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
1318   *ceed = op->ceed;
1319   return CEED_ERROR_SUCCESS;
1320 }
1321 
1322 /**
1323   @brief Get the number of elements associated with a CeedOperator
1324 
1325   @param[in]  op       CeedOperator
1326   @param[out] num_elem Variable to store number of elements
1327 
1328   @return An error code: 0 - success, otherwise - failure
1329 
1330   @ref Advanced
1331 **/
1332 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1333   if (op->is_composite) {
1334     // LCOV_EXCL_START
1335     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
1336     // LCOV_EXCL_STOP
1337   }
1338   *num_elem = op->num_elem;
1339   return CEED_ERROR_SUCCESS;
1340 }
1341 
1342 /**
1343   @brief Get the number of quadrature points associated with a CeedOperator
1344 
1345   @param[in]  op       CeedOperator
1346   @param[out] num_qpts Variable to store vector number of quadrature points
1347 
1348   @return An error code: 0 - success, otherwise - failure
1349 
1350   @ref Advanced
1351 **/
1352 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1353   if (op->is_composite) {
1354     // LCOV_EXCL_START
1355     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator");
1356     // LCOV_EXCL_STOP
1357   }
1358   *num_qpts = op->num_qpts;
1359   return CEED_ERROR_SUCCESS;
1360 }
1361 
1362 /**
1363   @brief Estimate number of FLOPs required to apply CeedOperator on the active vector
1364 
1365   @param[in]  op    CeedOperator to estimate FLOPs for
1366   @param[out] flops Address of variable to hold FLOPs estimate
1367 
1368   @ref Backend
1369 **/
1370 int CeedOperatorGetFlopsEstimate(CeedOperator op, CeedSize *flops) {
1371   bool is_composite;
1372   CeedCall(CeedOperatorCheckReady(op));
1373 
1374   *flops = 0;
1375   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1376   if (is_composite) {
1377     CeedInt num_suboperators;
1378     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1379     CeedOperator *sub_operators;
1380     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1381 
1382     // FLOPs for each suboperator
1383     for (CeedInt i = 0; i < num_suboperators; i++) {
1384       CeedSize suboperator_flops;
1385       CeedCall(CeedOperatorGetFlopsEstimate(sub_operators[i], &suboperator_flops));
1386       *flops += suboperator_flops;
1387     }
1388   } else {
1389     CeedInt            num_input_fields, num_output_fields;
1390     CeedOperatorField *input_fields, *output_fields;
1391 
1392     CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields));
1393 
1394     CeedInt num_elem = 0;
1395     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
1396     // Input FLOPs
1397     for (CeedInt i = 0; i < num_input_fields; i++) {
1398       if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1399         CeedSize restr_flops, basis_flops;
1400 
1401         CeedCall(CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_rstr, CEED_NOTRANSPOSE, &restr_flops));
1402         *flops += restr_flops;
1403         CeedCall(CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE, op->qf->input_fields[i]->eval_mode, &basis_flops));
1404         *flops += basis_flops * num_elem;
1405       }
1406     }
1407     // QF FLOPs
1408     CeedInt  num_qpts;
1409     CeedSize qf_flops;
1410     CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts));
1411     CeedCall(CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops));
1412     *flops += num_elem * num_qpts * qf_flops;
1413     // Output FLOPs
1414     for (CeedInt i = 0; i < num_output_fields; i++) {
1415       if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1416         CeedSize restr_flops, basis_flops;
1417 
1418         CeedCall(CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_rstr, CEED_TRANSPOSE, &restr_flops));
1419         *flops += restr_flops;
1420         CeedCall(CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE, op->qf->output_fields[i]->eval_mode, &basis_flops));
1421         *flops += basis_flops * num_elem;
1422       }
1423     }
1424   }
1425 
1426   return CEED_ERROR_SUCCESS;
1427 }
1428 
1429 /**
1430   @brief Get CeedQFunction global context for a CeedOperator.
1431            The caller is responsible for destroying `ctx` returned from this function via `CeedQFunctionContextDestroy()`.
1432 
1433            Note: If the value of `ctx` passed into this function is non-NULL, then it is assumed that `ctx` is a pointer to a
1434              CeedQFunctionContext. This CeedQFunctionContext will be destroyed if `ctx` is the only reference to this CeedQFunctionContext.
1435 
1436   @param[in]  qf  CeedQFunction
1437   @param[out] ctx Variable to store CeedQFunctionContext
1438 
1439   @return An error code: 0 - success, otherwise - failure
1440 
1441   @ref Backend
1442 **/
1443 int CeedOperatorGetContext(CeedOperator op, CeedQFunctionContext *ctx) {
1444   bool is_composite;
1445 
1446   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1447   if (is_composite) return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot retrieve QFunctionContext for composite operator");
1448 
1449   if (op->qf->ctx) CeedCall(CeedQFunctionContextReferenceCopy(op->qf->ctx, ctx));
1450   else *ctx = NULL;
1451   return CEED_ERROR_SUCCESS;
1452 }
1453 
1454 /**
1455   @brief Get label for a registered QFunctionContext field, or `NULL` if no field has been registered with this `field_name`.
1456 
1457   @param[in]  op          CeedOperator
1458   @param[in]  field_name  Name of field to retrieve label
1459   @param[out] field_label Variable to field label
1460 
1461   @return An error code: 0 - success, otherwise - failure
1462 
1463   @ref User
1464 **/
1465 int CeedOperatorContextGetFieldLabel(CeedOperator op, const char *field_name, CeedContextFieldLabel *field_label) {
1466   bool is_composite;
1467   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1468 
1469   if (is_composite) {
1470     // Check if composite label already created
1471     for (CeedInt i = 0; i < op->num_context_labels; i++) {
1472       if (!strcmp(op->context_labels[i]->name, field_name)) {
1473         *field_label = op->context_labels[i];
1474         return CEED_ERROR_SUCCESS;
1475       }
1476     }
1477 
1478     // Create composite label if needed
1479     CeedInt               num_sub;
1480     CeedOperator         *sub_operators;
1481     CeedContextFieldLabel new_field_label;
1482 
1483     CeedCall(CeedCalloc(1, &new_field_label));
1484     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
1485     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1486     CeedCall(CeedCalloc(num_sub, &new_field_label->sub_labels));
1487     new_field_label->num_sub_labels = num_sub;
1488 
1489     bool label_found = false;
1490     for (CeedInt i = 0; i < num_sub; i++) {
1491       if (sub_operators[i]->qf->ctx) {
1492         CeedContextFieldLabel new_field_label_i;
1493         CeedCall(CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name, &new_field_label_i));
1494         if (new_field_label_i) {
1495           label_found                    = true;
1496           new_field_label->sub_labels[i] = new_field_label_i;
1497           new_field_label->name          = new_field_label_i->name;
1498           new_field_label->description   = new_field_label_i->description;
1499           if (new_field_label->type && new_field_label->type != new_field_label_i->type) {
1500             // LCOV_EXCL_START
1501             CeedCall(CeedFree(&new_field_label));
1502             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field types on sub-operator contexts. %s != %s",
1503                              CeedContextFieldTypes[new_field_label->type], CeedContextFieldTypes[new_field_label_i->type]);
1504             // LCOV_EXCL_STOP
1505           } else {
1506             new_field_label->type = new_field_label_i->type;
1507           }
1508           if (new_field_label->num_values != 0 && new_field_label->num_values != new_field_label_i->num_values) {
1509             // LCOV_EXCL_START
1510             CeedCall(CeedFree(&new_field_label));
1511             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field number of values on sub-operator contexts. %ld != %ld",
1512                              new_field_label->num_values, new_field_label_i->num_values);
1513             // LCOV_EXCL_STOP
1514           } else {
1515             new_field_label->num_values = new_field_label_i->num_values;
1516           }
1517         }
1518       }
1519     }
1520     if (!label_found) {
1521       // LCOV_EXCL_START
1522       CeedCall(CeedFree(&new_field_label->sub_labels));
1523       CeedCall(CeedFree(&new_field_label));
1524       *field_label = NULL;
1525       // LCOV_EXCL_STOP
1526     } else {
1527       // Move new composite label to operator
1528       if (op->num_context_labels == 0) {
1529         CeedCall(CeedCalloc(1, &op->context_labels));
1530         op->max_context_labels = 1;
1531       } else if (op->num_context_labels == op->max_context_labels) {
1532         CeedCall(CeedRealloc(2 * op->num_context_labels, &op->context_labels));
1533         op->max_context_labels *= 2;
1534       }
1535       op->context_labels[op->num_context_labels] = new_field_label;
1536       *field_label                               = new_field_label;
1537       op->num_context_labels++;
1538     }
1539 
1540     return CEED_ERROR_SUCCESS;
1541   } else {
1542     return CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label);
1543   }
1544 }
1545 
1546 /**
1547   @brief Set QFunctionContext field holding double precision values.
1548            For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`.
1549 
1550   @param[in,out] op          CeedOperator
1551   @param[in]     field_label Label of field to set
1552   @param[in]     values      Values to set
1553 
1554   @return An error code: 0 - success, otherwise - failure
1555 
1556   @ref User
1557 **/
1558 int CeedOperatorContextSetDouble(CeedOperator op, CeedContextFieldLabel field_label, double *values) {
1559   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values);
1560 }
1561 
1562 /**
1563   @brief Get QFunctionContext field holding double precision values, read-only.
1564            For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`.
1565 
1566   @param[in]  op          CeedOperator
1567   @param[in]  field_label Label of field to get
1568   @param[out] num_values  Number of values in the field label
1569   @param[out] values      Pointer to context values
1570 
1571   @return An error code: 0 - success, otherwise - failure
1572 
1573   @ref User
1574 **/
1575 int CeedOperatorContextGetDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const double **values) {
1576   return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, num_values, values);
1577 }
1578 
1579 /**
1580   @brief Restore QFunctionContext field holding double precision values, read-only.
1581 
1582   @param[in]  op          CeedOperator
1583   @param[in]  field_label Label of field to restore
1584   @param[out] values      Pointer to context values
1585 
1586   @return An error code: 0 - success, otherwise - failure
1587 
1588   @ref User
1589 **/
1590 int CeedOperatorContextRestoreDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, const double **values) {
1591   return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values);
1592 }
1593 
1594 /**
1595   @brief Set QFunctionContext field holding int32 values.
1596            For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`.
1597 
1598   @param[in,out] op          CeedOperator
1599   @param[in]     field_label Label of field to set
1600   @param[in]     values      Values to set
1601 
1602   @return An error code: 0 - success, otherwise - failure
1603 
1604   @ref User
1605 **/
1606 int CeedOperatorContextSetInt32(CeedOperator op, CeedContextFieldLabel field_label, int *values) {
1607   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32, values);
1608 }
1609 
1610 /**
1611   @brief Get QFunctionContext field holding int32 values, read-only.
1612            For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`.
1613 
1614   @param[in]  op          CeedOperator
1615   @param[in]  field_label Label of field to get
1616   @param[out] num_values  Number of int32 values in `values`
1617   @param[out] values      Pointer to context values
1618 
1619   @return An error code: 0 - success, otherwise - failure
1620 
1621   @ref User
1622 **/
1623 int CeedOperatorContextGetInt32Read(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const int **values) {
1624   return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, num_values, values);
1625 }
1626 
1627 /**
1628   @brief Restore QFunctionContext field holding int32 values, read-only.
1629 
1630   @param[in]  op          CeedOperator
1631   @param[in]  field_label Label of field to get
1632   @param[out] values      Pointer to context values
1633 
1634   @return An error code: 0 - success, otherwise - failure
1635 
1636   @ref User
1637 **/
1638 int CeedOperatorContextRestoreInt32Read(CeedOperator op, CeedContextFieldLabel field_label, const int **values) {
1639   return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, values);
1640 }
1641 
1642 /**
1643   @brief Apply CeedOperator to a vector
1644 
1645   This computes the action of the operator on the specified (active) input, yielding its (active) output.
1646   All inputs and outputs must be specified using CeedOperatorSetField().
1647 
1648   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1649 
1650   @param[in]  op      CeedOperator to apply
1651   @param[in]  in      CeedVector containing input state or @ref CEED_VECTOR_NONE if there are no active inputs
1652   @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
1653 outputs
1654   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1655 
1656   @return An error code: 0 - success, otherwise - failure
1657 
1658   @ref User
1659 **/
1660 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) {
1661   CeedCall(CeedOperatorCheckReady(op));
1662 
1663   if (op->num_elem) {
1664     // Standard Operator
1665     if (op->Apply) {
1666       CeedCall(op->Apply(op, in, out, request));
1667     } else {
1668       // Zero all output vectors
1669       CeedQFunction qf = op->qf;
1670       for (CeedInt i = 0; i < qf->num_output_fields; i++) {
1671         CeedVector vec = op->output_fields[i]->vec;
1672         if (vec == CEED_VECTOR_ACTIVE) vec = out;
1673         if (vec != CEED_VECTOR_NONE) {
1674           CeedCall(CeedVectorSetValue(vec, 0.0));
1675         }
1676       }
1677       // Apply
1678       CeedCall(op->ApplyAdd(op, in, out, request));
1679     }
1680   } else if (op->is_composite) {
1681     // Composite Operator
1682     if (op->ApplyComposite) {
1683       CeedCall(op->ApplyComposite(op, in, out, request));
1684     } else {
1685       CeedInt num_suboperators;
1686       CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1687       CeedOperator *sub_operators;
1688       CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1689 
1690       // Zero all output vectors
1691       if (out != CEED_VECTOR_NONE) {
1692         CeedCall(CeedVectorSetValue(out, 0.0));
1693       }
1694       for (CeedInt i = 0; i < num_suboperators; i++) {
1695         for (CeedInt j = 0; j < sub_operators[i]->qf->num_output_fields; j++) {
1696           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1697           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1698             CeedCall(CeedVectorSetValue(vec, 0.0));
1699           }
1700         }
1701       }
1702       // Apply
1703       for (CeedInt i = 0; i < op->num_suboperators; i++) {
1704         CeedCall(CeedOperatorApplyAdd(op->sub_operators[i], in, out, request));
1705       }
1706     }
1707   }
1708   return CEED_ERROR_SUCCESS;
1709 }
1710 
1711 /**
1712   @brief Apply CeedOperator to a vector and add result to output vector
1713 
1714   This computes the action of the operator on the specified (active) input, yielding its (active) output.
1715   All inputs and outputs must be specified using CeedOperatorSetField().
1716 
1717   @param[in]  op      CeedOperator to apply
1718   @param[in]  in      CeedVector containing input state or NULL if there are no active inputs
1719   @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
1720   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1721 
1722   @return An error code: 0 - success, otherwise - failure
1723 
1724   @ref User
1725 **/
1726 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) {
1727   CeedCall(CeedOperatorCheckReady(op));
1728 
1729   if (op->num_elem) {
1730     // Standard Operator
1731     CeedCall(op->ApplyAdd(op, in, out, request));
1732   } else if (op->is_composite) {
1733     // Composite Operator
1734     if (op->ApplyAddComposite) {
1735       CeedCall(op->ApplyAddComposite(op, in, out, request));
1736     } else {
1737       CeedInt num_suboperators;
1738       CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1739       CeedOperator *sub_operators;
1740       CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
1741 
1742       for (CeedInt i = 0; i < num_suboperators; i++) {
1743         CeedCall(CeedOperatorApplyAdd(sub_operators[i], in, out, request));
1744       }
1745     }
1746   }
1747   return CEED_ERROR_SUCCESS;
1748 }
1749 
1750 /**
1751   @brief Destroy a CeedOperator
1752 
1753   @param[in,out] op CeedOperator to destroy
1754 
1755   @return An error code: 0 - success, otherwise - failure
1756 
1757   @ref User
1758 **/
1759 int CeedOperatorDestroy(CeedOperator *op) {
1760   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1761   if ((*op)->Destroy) CeedCall((*op)->Destroy(*op));
1762   CeedCall(CeedDestroy(&(*op)->ceed));
1763   // Free fields
1764   for (CeedInt i = 0; i < (*op)->num_fields; i++) {
1765     if ((*op)->input_fields[i]) {
1766       if ((*op)->input_fields[i]->elem_rstr != CEED_ELEMRESTRICTION_NONE) {
1767         CeedCall(CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_rstr));
1768       }
1769       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1770         CeedCall(CeedBasisDestroy(&(*op)->input_fields[i]->basis));
1771       }
1772       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->input_fields[i]->vec != CEED_VECTOR_NONE) {
1773         CeedCall(CeedVectorDestroy(&(*op)->input_fields[i]->vec));
1774       }
1775       CeedCall(CeedFree(&(*op)->input_fields[i]->field_name));
1776       CeedCall(CeedFree(&(*op)->input_fields[i]));
1777     }
1778   }
1779   for (CeedInt i = 0; i < (*op)->num_fields; i++) {
1780     if ((*op)->output_fields[i]) {
1781       CeedCall(CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_rstr));
1782       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1783         CeedCall(CeedBasisDestroy(&(*op)->output_fields[i]->basis));
1784       }
1785       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->output_fields[i]->vec != CEED_VECTOR_NONE) {
1786         CeedCall(CeedVectorDestroy(&(*op)->output_fields[i]->vec));
1787       }
1788       CeedCall(CeedFree(&(*op)->output_fields[i]->field_name));
1789       CeedCall(CeedFree(&(*op)->output_fields[i]));
1790     }
1791   }
1792   // Destroy sub_operators
1793   for (CeedInt i = 0; i < (*op)->num_suboperators; i++) {
1794     if ((*op)->sub_operators[i]) {
1795       CeedCall(CeedOperatorDestroy(&(*op)->sub_operators[i]));
1796     }
1797   }
1798   CeedCall(CeedQFunctionDestroy(&(*op)->qf));
1799   CeedCall(CeedQFunctionDestroy(&(*op)->dqf));
1800   CeedCall(CeedQFunctionDestroy(&(*op)->dqfT));
1801   // Destroy any composite labels
1802   for (CeedInt i = 0; i < (*op)->num_context_labels; i++) {
1803     CeedCall(CeedFree(&(*op)->context_labels[i]->sub_labels));
1804     CeedCall(CeedFree(&(*op)->context_labels[i]));
1805   }
1806   CeedCall(CeedFree(&(*op)->context_labels));
1807 
1808   // Destroy fallback
1809   CeedCall(CeedOperatorDestroy(&(*op)->op_fallback));
1810 
1811   // Destroy assembly data
1812   CeedCall(CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled));
1813   CeedCall(CeedOperatorAssemblyDataDestroy(&(*op)->op_assembled));
1814 
1815   CeedCall(CeedFree(&(*op)->input_fields));
1816   CeedCall(CeedFree(&(*op)->output_fields));
1817   CeedCall(CeedFree(&(*op)->sub_operators));
1818   CeedCall(CeedFree(&(*op)->name));
1819   CeedCall(CeedFree(op));
1820   return CEED_ERROR_SUCCESS;
1821 }
1822 
1823 /// @}
1824