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