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