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